#include "mainwindow.h"
#include "ui_mainwindow.h"



#define     ON                          1       //开
#define     OFF                         0       //关
//#define     DEBUG_SWITCH                ON      //打印信息输出开关
#define     DEBUG_SWITCH                OFF     //打印信息输出开关

#define     DEFAULT_TAG                 "All messages"          //默认的日志Tag，即显示所有信息
#define     ADB_ERR_RESULT              "'adb' "                //ADB返回的错误信息
#define     MAX_CACHE_LINES             500000                  //日志文件最大缓存数据行数
#define     MAX_TERNIMAL_CACHE_LINES    500000                  //实时终端模式下日志最大缓存数据行数
#define     LOG_CACHE_FILE_PATH         "/cache/logCache.log"   //日志缓存文件路径
#define     LOG_REBUILD_FILE_PATH       "/cache/rebuild.log"    //重构日志缓存文件的路径
#define     SETTING_FILE_PATH           "/config/setting.cfg"   //软件的全局配置文件
#define     TEMP_SETTING_FILE_PATH      "/config/temp.cfg"      //软件的全局配置文件的临时文件
#define     BACKUP_SETTING_FILE_PATH    "/config/backup_setting.cfg"   //软件的全局配置文件的备份文件

#define     WORK_MODE_OFFLINE           "  离线查看模式  "        //工作模式--离线查看模式
#define     WORK_MODE_ONLINE            "  实时终端模式  "        //工作模式--实时终端模式
#define     NO_ADB_DEVICE               "  ADB设备未连接  "       //无ADB设备

#define     MAX_SAVE_HISTORY_NUM        10                      //最大文件打开历史记录数量
#define     DEFAULT_HISTORY_ACTION      "清空历史记录"            //清空文件打开历史记录Action动作名称

#define     LED_ID_ADB_STATUS           1                       //ADB连接状态指示灯
#define     MAX_CACHE_FILE_SIZE         (MAX_CACHE_LINES * 102) //根据最大缓存行数预估的最大文件大小（大致每10行为1K）
#define     ALREADY_CMD_EXEC            "Already cmd exec"      //已经有命令在执行


QRegExp regExp1("(\\d\\d-\\d\\d\\s\\d\\d:\\d\\d:\\d\\d\\.\\d+)\\s+(\\d*)\\s*(\\S+)\\s([VDIWEAF])\\s([^:]+):(.+)");
QRegExp regExp2("(\\d\\d-\\d\\d\\s\\d\\d:\\d\\d:\\d\\d\\.\\d+): ([VDIWEAF])/([^\\(]+)\\((\\d+\\)):(.+)");
QRegExp regExp3("(\\d\\d-\\d\\d\\s\\d\\d:\\d\\d:\\d\\d\\.\\d+)\\s([VDIWEAF])/([^\\(]+)\\(\\s*(\\d+)\\):(.+)");
QRegExp regExp4("(\\d\\d-\\d\\d\\s\\d\\d:\\d\\d:\\d\\d\\.\\d+)\\s([VDIWEAF])/([^\\(]+)\\(\\s*(\\d+)\\):(.+)");


/*
 * 函数名称：    MainWindow(QWidget *parent)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.1.20
 * 函数功能：    构造函数
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow) {
    //软件UI属性配置
    ui->setupUi(this);                      //装在UI文件
    setWindowTitle("Android日志分析工具 V3.5.3");    //设置软件标题
    setWindowState(Qt::WindowMaximized);    //设置软件启动时为最大化窗口
    //初始化软件运行环境
    initEnvironment();
    //设置信号与槽的连接
    QObject::connect(ui->menuBar, SIGNAL(triggered(QAction *)), this, SLOT(trigerMenuSlot(QAction *)));
    QObject::connect(ui->cbLevel, SIGNAL(currentIndexChanged(int)), this, SLOT(logLevelChangedSlot()));
    QObject::connect(ui->etSearch, SIGNAL(returnPressed()), this, SLOT(searchEditChangedSlot()));
    QObject::connect(ui->swTimeFilter, SIGNAL(clicked()), this, SLOT(swTimeFilterChangedSlot()));
    QObject::connect(ui->swTime, SIGNAL(clicked()), this, SLOT(swMessageFilterChangedSlot()));
    QObject::connect(ui->swLevel, SIGNAL(clicked()), this, SLOT(swMessageFilterChangedSlot()));
    QObject::connect(ui->swPID, SIGNAL(clicked()), this, SLOT(swMessageFilterChangedSlot()));
    QObject::connect(ui->swTag, SIGNAL(clicked()), this, SLOT(swMessageFilterChangedSlot()));
    QObject::connect(ui->swHead, SIGNAL(clicked()), this, SLOT(swMessageFilterChangedSlot()));
    QObject::connect(ui->history, SIGNAL(triggered(QAction *)), this, SLOT(trigerHistorySlot(QAction *)));
    QObject::connect(ui->cbPID, SIGNAL(currentIndexChanged(int)), this, SLOT(cbPIDChangedSlot()));
    QObject::connect(ui->cbPage, SIGNAL(currentIndexChanged(int)), this, SLOT(gotoPageSlot()));
    QObject::connect(ui->lwContent, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)),
                     this, SLOT(curContentChangedSlot()));
    isConnectTimeFilter(true);  //建立时间过滤器的信号与槽的连接
    isConnectScroll(true);      //建立内容显示区的垂直滚动条的信号与槽的连接
    startTimer->start(500);
}


/*
 * 函数名称：    ~MainWindow()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.1.20
 * 函数功能：    析构函数，用于释放软件所占用的资源
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
MainWindow::~MainWindow() {
    delete ui;
}


/*
 * 函数名称：    loadSoftwareSetting()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.3.16
 * 函数功能：    加载软件的设置文件
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::loadSoftwareSetting(QString path) {
    //创建配置文件解析器
    mSettingParse = new ParseSettingFile(this, path);
    //从解析器中获取解析后的参数
    mCurFontType = mSettingParse->getFontType();
    mCurFontSize = mSettingParse->getFontSize();
    mCurCodeType = mSettingParse->getCodeType();
    ADBPath = mSettingParse->getADBPath();
    mRegisterNum = mSettingParse->getRegisterNum();
    mHistory = mSettingParse->getHistory();
    isApplyOfflineMode = mSettingParse->getIsApplyOfflineMode();
    isApplyRealtimeMode = mSettingParse->getIsApplyRealtimeMode();
    isFirstFilter = mSettingParse->getIsFirstFilter();
    isSecondFilter = mSettingParse->getIsSecondFilter();
    mFirstSysFilters = mSettingParse->getFirstSysFilters();
    mSecondSysFilters = mSettingParse->getSecondSysFilters();
    isApplyDebugApp = mSettingParse->getIsApplyDebugApp();
    mCurDebugApp = mSettingParse->getCurDebugApp();
    Security *security = Security::getInstance();
    if (security->checkRegisterNum(mRegisterNum)) {
        security->setRegisted(true);
    } else {
        security->setRegisted(false);
    }
    updateHistoryAction(mHistory);
}


/*
 * 函数名称：    initEnvironment()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.3.14
 * 函数功能：    初始化相关变量
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::initEnvironment() {
    //此处变量定义必须放在最前面
    mEventLoop = new QEventLoop;
    eventLoopTimer = new QTimer(this);
    QObject::connect(eventLoopTimer, SIGNAL(timeout()), this, SLOT(eventLoopTimeoutSlot()));
    mWindowsUserPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/LogCatTool";
    QDir dir(mWindowsUserPath);
    if (!dir.exists()) {
        dir.mkdir(mWindowsUserPath);
    }
    logCurTime("我的文档路径：" + mWindowsUserPath);
    mLogCacheFilePath = QCoreApplication::applicationDirPath() + LOG_CACHE_FILE_PATH;
    logCurTime("当前日志缓存路径：" + mLogCacheFilePath);
    //读取日志缓存目录，构建备份文件数据表mBackupCacheFilePath
    QString tempPath = QCoreApplication::applicationDirPath() + "/cache";
    QDir tempDir(tempPath);
    QFileInfoList infoList = tempDir.entryInfoList(QDir::Files);
    for (int i = 0; i < infoList.size(); ++i) {
        QString name = infoList.at(i).baseName();
        if (name.contains("-") && (name.split("-").at(0) == "logCache")) {
            mBackupCacheFilePath.append(infoList.at(i).absoluteFilePath());
        } else if (name.contains("rebuild")) {
            mBackupCacheFilePath.append(infoList.at(i).absoluteFilePath());
        }
    }
    mSettingFilePath = mWindowsUserPath + SETTING_FILE_PATH;
    logCurTime("当前全局设置文件路径：" + mSettingFilePath);
    //加载软件的设置文件，并设置相应的参数
    loadSoftwareSetting(mSettingFilePath);
    //初始化文件打开历史记录
    if (ui->history->actions().size() > 2) {
        ui->history->setEnabled(true);
    } else {
        ui->history->setEnabled(false);
    }
    //创建软件底部状态栏的标签
    fileLabel = new QLabel;
    adbStatusLabel = new QLabel;
    adbDeviceLabel = new QLabel;
    workModeLabel = new QLabel;
    curDebugAppLabel = new QLabel;
    adbStatusLabel->setFixedSize(15, 15);
    //样式表的设置
    ui->statusBar->setStyleSheet("QStatusBar::item{border:0px;}"
                                 "QStatusBar{background-color:rgb(54,54,54);}");
    fileLabel->setStyleSheet("background-color:rgb(51,51,51);color:rgb(200,200,200);");
    adbDeviceLabel->setStyleSheet("background-color:rgb(51,51,51);color:rgb(200,200,200);");
    workModeLabel->setStyleSheet("background-color:rgb(51,51,51);color:rgb(200,200,200);");
    curDebugAppLabel->setStyleSheet("background-color:rgb(51,51,51);color:rgb(200,200,200);");
    adbDeviceLabel->setMaximumWidth(600);
    fileLabel->setMaximumWidth(600);
    curDebugAppLabel->setMaximumWidth(400);
    ui->statusBar->addWidget(workModeLabel);
    ui->statusBar->addWidget(adbStatusLabel);
    ui->statusBar->addWidget(adbDeviceLabel);
    ui->statusBar->addWidget(fileLabel);
    ui->statusBar->addWidget(curDebugAppLabel);
    //软件启动时默认为“离线查看模式”
    adbDeviceLabel->setText(NO_ADB_DEVICE);
    ledOff(LED_ID_ADB_STATUS);
    workModeLabel->setText(WORK_MODE_OFFLINE);
    //时间信息过滤器默认失能
    ui->swTimeFilter->setChecked(false);
    //获取UI控件对象
    lwFilter = ui->lwFilter;
    lwContent = ui->lwContent;
    labelLine = ui->labelLine;
    labelTime = ui->labelTime;
    labelLevel = ui->labelLevel;
    labelPID = ui->labelPID;
    labelTID = ui->labelTID;
    labelTag = ui->labelTag;
    labelText = ui->labelText;
    //相关变量初始化
    isDispTime = true;
    isDispLevel = true;
    isDispPID = true;
    isDispTID = true;
    isDispTag = true;
    isDispHead = true;
    logFileSize = 0;
    mCurLogFilePath = "";
    mLastSearchText = "";
    mCurDebugAppPID = "";
    cmdProcess = NULL;
    mCurADBDeviceStatus = false;
    isTerminalMode = false;
    mLastScrollValue = 0;
    isCheckedUpdateFile = false;
    mReConnecCnt = 0;
    isLoadingFile = false;
    isScaningADBDevice = false;
    mFilterChangedFlag = false;
    isLedOn = false;
    isSearching = false;
    isExitSearch = false;
    isPageOffsetMode = false;
    isNeedBackupCacheFile = false;
    isPeopleExitTerminal = false;
    isStartADBServer = false;
    isExitEventLoop = false;
    isCheckingPID = false;
    isClickRestart = false;
    mGotoLine = "";
    mAllLines = 0;
    mCurLines = 0;
    mLastPos = 0;
    mCurPageIndex = 0;
    mPageNum = 0;
    mLogType = LOG_TYPE_UNKNOWN;
    clearAllPageOffset();
    mCurOptDialog = NULL;
    mWaitDialog = new QWaitDialog(this, "");
    mTagAnalyseDialog = new TagAnalyse;
    mCurRange = new ItemsRange(this, 0, 0, 0, 0, 0);
    ui->verticalScrollBar->setHidden(true);
    ui->labelPage->setHidden(true);
    ui->cbPage->setHidden(true);
    startTimer = new QTimer(this);
    QObject::connect(startTimer, SIGNAL(timeout()), this, SLOT(startTimeoutSlot()));
    fileTimer = new QTimer(this);
    QObject::connect(fileTimer, SIGNAL(timeout()), this, SLOT(fileTimeoutSlot()));
    statusTimer = new QTimer(this);
    QObject::connect(statusTimer, SIGNAL(timeout()), this, SLOT(statusTimeoutSlot()));
    clearAllDataList();      //初始化全局数据表
    //设置文本字体样式
    QFont font = QFont(mCurFontType, mCurFontSize, QFont::Normal);
    lwFilter->setSelectionMode(QAbstractItemView::ExtendedSelection);
    lwFilter->setFont(font);
    lwFilter->setStyleSheet("QListWidget{color:#FFC5C5C5;}"
                            "QListWidget::Item:selected{background:#80ABABAB;}"
                            "QListWidget::Item:hover{background:#40ABABAB;}");
    lwContent->setFont(font);
    lwContent->setStyleSheet("QListWidget::Item:selected{background:#80ABABAB;}"
                             "QListWidget::Item:hover{background:#40ABABAB;}");
    labelLine->setFont(font);
    labelTime->setFont(font);
    labelLevel->setFont(font);
    labelPID->setFont(font);
    labelTID->setFont(font);
    labelTag->setFont(font);
    labelText->setFont(font);
    //默认离线模式下禁止清空当前显示按钮
    ui->actionClearCurDisp->setEnabled(false);
    //创建日志等级颜色配色表
    QString colorStr = "#0843CE,#007BB9,#007F00,#FF7F00,#FF0000,#A50000,#FFFFFF";
    QStringList corlorList = colorStr.split(",");
    mColorConfig = new QList<QColor>;
    for (int i = 0; i < 7; ++i) {
        mColorConfig->append(QColor(corlorList.at(i)));
    }
}





/*******************************************************************************************
 *                                    普通槽函数区域
 * *****************************************************************************************/

/*
 * 函数名称：    trigerMenuSlot(QAction* action)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.4
 * 函数功能：    软件菜单栏触发槽函数
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::trigerMenuSlot(QAction *action) {
    /*****************    文件菜单    *********************/
    //打开文件
    if (action == ui->actionOpen) {
        openFileAction();
    }
    //重载文件动作
    else if (action == ui->actionRefresh) {
        refreshLogFileAction();
    }
    //关闭文件
    else if (action == ui->actionClose) {
        closeFileAction();
    }
    //重启软件
    else if (action == ui->actionRestart) {
        restartSoftwareAction();
    }
    //退出软件
    else if (action == ui->actionExit) {
        close();
    }
    /*****************    搜索菜单    *********************/
    //跳转到行
    else if (action == ui->actionGotoLine) {
        gotoLineAction();
    }
    //跳转到上一页
    else if (action == ui->actionLastPage) {
        lastPageAction();
    }
    //跳转到下一页
    else if (action == ui->actionNextPage) {
        nextPageAction();
    }
    //页分界切换
    else if (action == ui->actionPageOffset) {
        pageOffsetAction();
    }
    /*****************    工具菜单    *********************/
    //ADB连接动作
    else if (action == ui->actionConnectADB) {
        connectADBAction();
    }
    //工作模式切换动作
    else if (action == ui->actionTerminal) {
        clickTerminalAction();
    }
    //Tag分析
    else if (action == ui->actionTagAnalyse) {
        openTagAnalyseWindowsAction();
    }
    //清空当前显示
    else if (action == ui->actionClearCurDisp) {
        clearCurDispAction();
    }
    //安装APK到设备
    else if (action == ui->actionInstallAPK) {
        installAPKAction();
    }
    /*****************    设置菜单    *********************/
    //选项配置
    else if (action == ui->actionSetting) {
        setingAction();
    }
    //备份配置文件
    else if (action == ui->actionBackup) {
        backupSettingFileAction();
    }
    //导入配置文件
    else if (action == ui->actionImport) {
        importSettingFileAction();
    }
    /*****************    帮助菜单    *********************/
    //关于软件
    else if (action == ui->actionAbout) {
        aboutAction();
    }
    //软件注册
    else if (action == ui->actionRegister) {
        registerSolftwareAction();
    }
}


/*
 * 函数名称：    trigerHistorySlot(QAction *action)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.3.16
 * 函数功能：    文件打开历史记录触发槽函数
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::trigerHistorySlot(QAction *action) {
    //如果是清空历史记录
    if (action->text() == DEFAULT_HISTORY_ACTION) {
        QList<QAction *> list = ui->history->actions();
        QMenu *history = ui->history;
        int size = list.size();
        for (int i = 0; i < (size - 1); ++i) {
            history->removeAction(list.at(i));
        }
        history->setEnabled(false);
    }
    //否则为打开选中的文件
    else {
        QString path = action->text();
        //如果选中的文件还未打开
        if (path != mCurLogFilePath) {
            resetPageParams();
            loadFile(path);
        } else {
            QMessageBox::information(this, "警告", "该文件已经打开！");
        }
    }
}


/*
 * 函数名称：    logLevelChangedSlot()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.4
 * 函数功能：    日志等级发生了改变
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::logLevelChangedSlot() {
    filterChangedSlot();
}


/*
 * 函数名称：    filterChangedSlot()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.4
 * 函数功能：    当前选中的过滤器发生了变化
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::filterChangedSlot() {
    mFilterChangedFlag = true;
    dispContainSearchString(ui->etSearch->text());
    //如果当前是实时终端模式，切换后显示内容的最后一行，以便达到显示最新内容的目的
    if (isTerminalMode) {
        ui->verticalScrollBar->setValue(ui->verticalScrollBar->maximum());
        if (lwContent->count() > 0) {
            lwContent->setCurrentRow(lwContent->count() - 1);
        }
    }
}


/*
 * 函数名称：    searchEditChangedSlot()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.4
 * 函数功能：    日志搜索栏发生了改变
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::searchEditChangedSlot() {
    //如果搜索内容未发生变化，则无需重复搜索
    if (mLastSearchText == ui->etSearch->text()) {
        return;
    }
    logCurTime("开始搜索...");
    //创建并显示加载进度条
    isSearching = true;
    mSearchDialog = new QProgressDialog;
    QObject::connect(mSearchDialog, SIGNAL(canceled()), this, SLOT(exitSearchSlot()));
    mSearchDialog->setCancelButton(0);
    mSearchDialog->setLabelText("正在搜索，请稍后....");
    mSearchDialog->setMinimum(0);
    mSearchDialog->setMaximum(100);
    mSearchDialog->setValue(0);
    mSearchDialog->setModal(true);
    mSearchDialog->show();
    mainThreadWait_ms(10);  //阻塞主线程10ms，只有阻塞主线程，进度条才有机会显示出来
    //创建数据备份区
    QStringList backupCurTimes = mCurTimes;
    QStringList backupCurLevels = mCurLevels;
    QStringList backupCurPIDs = mCurPIDs;
    QStringList backupCurTIDs = mCurTIDs;
    QStringList backupCurTags = mCurTags;
    QStringList backupCurTexts = mCurTexts;
    //开始搜索
    dispContainSearchString(ui->etSearch->text());
    //如果用户在搜索过程中人为的停止了搜索，则恢复搜索前的数据
    if (isExitSearch) {
        mCurTimes = backupCurTimes;
        mCurLevels = backupCurLevels;
        mCurPIDs = backupCurPIDs;
        mCurTIDs = backupCurTIDs;
        mCurTags = backupCurTags;
        mCurTexts = backupCurTexts;
        mCurLines = mCurTags.size();
        updateCurRange();   //更新当前显示缓冲区范围
        //自动更新时间过滤器的起止时间显示
        if (mCurTimes.isEmpty()) {
            mStartTime = "01-01 00:00:00.000";
            mStopTime = "01-01 00:00:00.000";
        } else {
            mStartTime = mCurTimes.at(0);
            mStopTime = mCurTimes.at(mCurLines - 1);
        }
        updateTimeStringToWidget(mStartTime, mStopTime);
        //显示当前数据表
        dispCurDataList();
        logCurTime("退出搜索");
    } else {
        mSearchDialog->close();
        mainThreadWait_ms(10);
        logCurTime("搜索完毕");
    }
    //清空数据备份区
    backupCurTimes.clear();
    backupCurLevels.clear();
    backupCurPIDs.clear();
    backupCurTags.clear();
    backupCurTexts.clear();
    QObject::disconnect(mSearchDialog, SIGNAL(canceled()), this, SLOT(exitSearchSlot()));
    delete mSearchDialog;
    isExitSearch = false;
    isSearching = false;
    mLastSearchText = ui->etSearch->text();
}


/*
 * 函数名称：    swTimeFilterChangedSlot()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.9
 * 函数功能：    时间过滤开关变化槽函数
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::swTimeFilterChangedSlot() {
    //如果使能了时间过滤器
    if (ui->swTimeFilter->isChecked()) {
        //从控件中读取时间信息并进行格式化重构
        mStartTime = ui->cbStartMonth->currentText() + "-" + ui->cbStartDay->currentText()
                     + " " + ui->cbStartHour->currentText() + ":" + ui->cbStartMinute->currentText()
                     + ":" + ui->cbStartSecond->currentText() + ".000";
        mStopTime = ui->cbStopMonth->currentText() + "-" + ui->cbStopDay->currentText()
                    + " " + ui->cbStopHour->currentText() + ":" + ui->cbStopMinute->currentText()
                    + ":" + ui->cbStopSecond->currentText() + ".000";
        //如果起始时间大于终止时间
        if (QString::compare(mStartTime, mStopTime) > 0) {
            ui->swTimeFilter->setChecked(false);
            QMessageBox::information(this, "警告", "时间设置错误，应满足【起始时间 <= 终止时间】！");
            return;
        }
    }
    //如果失能了时间过滤器
    else {
        //若当前数据表为空则初始化为固定值，否则设置为整个时间区域
        if (mCurTimes.isEmpty()) {
            mStartTime = "01-01 00:00:00.000";
            mStopTime = "01-01 00:00:00.000";
        } else {
            mStartTime = mCurTimes.at(0);
            mStopTime = mCurTimes.at(mCurLines - 1);
        }
        //更新时间信息到控件显示
        updateTimeStringToWidget(mStartTime, mStopTime);
    }
    //刷新显示
    if (!mCurTimes.isEmpty()) {
        dispContainSearchString(ui->etSearch->text());
    }
}


/*
 * 函数名称：    timeFilterChangedSlot()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.9
 * 函数功能：    时间过滤选择框变化槽函数
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::timeFilterChangedSlot() {
    if (ui->swTimeFilter->isChecked()) {
        //从控件中读取时间信息并进行格式化重构
        mStartTime = ui->cbStartMonth->currentText() + "-" + ui->cbStartDay->currentText()
                     + " " + ui->cbStartHour->currentText() + ":" + ui->cbStartMinute->currentText()
                     + ":" + ui->cbStartSecond->currentText() + ".000";
        mStopTime = ui->cbStopMonth->currentText() + "-" + ui->cbStopDay->currentText()
                    + " " + ui->cbStopHour->currentText() + ":" + ui->cbStopMinute->currentText()
                    + ":" + ui->cbStopSecond->currentText() + ".000";
        //如果起始时间大于终止时间
        if (QString::compare(mStartTime, mStopTime) > 0) {
            ui->swTimeFilter->setChecked(false);
            QMessageBox::information(this, "警告", "时间设置错误，应满足【起始时间 <= 终止时间】！");
            return;
        }
        //刷新显示
        if (!mCurTimes.isEmpty()) {
            dispContainSearchString(ui->etSearch->text());
        }
    }
}


/*
 * 函数名称：    swMessageFilterChangedSlot()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.9
 * 函数功能：    信息过滤开关变化槽函数
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::swMessageFilterChangedSlot() {
    isDispTime = !ui->swTime->isChecked();
    isDispLevel = !ui->swLevel->isChecked();
    isDispPID = !ui->swPID->isChecked();
    isDispTag = !ui->swTag->isChecked();
    isDispHead = !ui->swHead->isChecked();
    //根据信息过滤器的开关状态设置是否显示对应的标签
    ui->labelTime->setVisible(isDispTime);
    ui->labelLevel->setVisible(isDispLevel);
    ui->labelPID->setVisible(isDispPID);
    ui->labelTID->setVisible(isDispTID);
    ui->labelTag->setVisible(isDispTag);
    //刷新显示
    dispContainSearchString(ui->etSearch->text());
}


/*
 * 函数名称：    threadDoneSlot()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.3.14
 * 函数功能：    执行ADB命令线程完毕槽函数
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::threadDoneSlot() {
    //logCurTime("线程执行完毕");
    //mEventLoop->exit();
    isExitEventLoop = true;
}


/*
 * 函数名称：    showDialogSlot()
 * 函数版本：        1.0.0
 * 函数版本：        1.0.1  【修复同时多次显示对话框而无法正常关闭，导致软件死机的BUG】
 * 作者：            HXL
 * 创建日期：    2017.3.14
 * 函数功能：    显示等待对话框槽函数
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::showDialogSlot() {
    //logCurTime("显示等待对话框");
    //避免重复显示，造成显示多个对话框无法关闭，引起软件死机的BUG
    if (mCurADBDeviceStatus || mWaitDialog->isVisible()) {
        //logCurTime("显示失败");
        return;
    }
    QString title;
    if (mCurADBDeviceStatus) {
        title = "正在重连ADB服务器....";
    } else {
        title = "正在启动ADB服务器....";
    }
    mReConnecCnt++;
    delete mWaitDialog;
    mWaitDialog = new QWaitDialog(this, title);
    mWaitDialog->exec();
}


/*
 * 函数名称：    closeDialogSlot()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.3.14
 * 函数功能：    关闭等待对话框槽函数
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::closeDialogSlot() {
    //logCurTime("关闭等待对话框");
    mWaitDialog->close();
}


/*
 * 函数名称：    gotoLineEditTextChangedSLot(QString text)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.3.16
 * 函数功能：    跳转到行输入编辑框当前文本发生变化触发槽函数
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::gotoLineEditTextChangedSLot(QString text) {
    mGotoLine = text;
}


/*
 * 函数名称：    gotoLineSlot()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.3.16
 * 函数功能：    跳转到指定行槽函数
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::gotoLineSlot() {
    QString text = mGotoLine;
    if (text.isEmpty()) {
        mCurOptDialog->close();
        return;
    }
    int length = text.length();
    QChar temp;
    for (int i = 0; i < length; ++i) {
        temp = text.at(i);
        if ((temp < '0') || (temp > '9')) {
            QMessageBox::information(this, "警告", "行号无效，必须为纯数字！");
            return;
        }
    }
    int line = text.toInt();
    if ((line <= 0) || (line > mCurLines)) {
        QMessageBox::information(this, "警告", "行号无效，不在指定的范围内！");
        return;
    }
    mCurOptDialog->close();
    //更新当前显示缓冲区范围
    int visibleFirst = line - mCurRange->getPageItemNum() / 2;
    int first = visibleFirst - 1;
    delete mCurRange;
    mCurRange = new ItemsRange(this, first, visibleFirst, mCurRange->getCount(),
                               mCurRange->getPageItemNum(), mCurLines);
    dispAreaData(mCurRange, 0); //显示缓冲区的数据
    //将焦点定位到跳转的行
    int count = mCurRange->getCount();
    int bitNum = QString::number(mCurLines).length();
    QListWidgetItem *item;
    for (int i = 0; i < count; ++i) {
        item = lwContent->item(i);
        if (item == NULL) {
            continue;
        } else if (line == item->text().mid(0, bitNum).toInt()) {
            isConnectScroll(false);
            lwContent->setCurrentRow(i);
            isConnectScroll(true);
            break;
        }
    }
}


/*
 * 函数名称：    cbPIDChangedSlot()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.4.20
 * 函数功能：    进程筛选框变化槽函数
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::cbPIDChangedSlot() {
    QObject::disconnect(ui->lwFilter, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)),
                        this, SLOT(filterChangedSlot()));
    updateCurFilters();
    //最后默认选中第一行，并显示所有日志内容
    if (lwFilter->count() > 0) {
        lwFilter->setCurrentRow(0);
    }
    updateCurRange();
    QObject::connect(ui->lwFilter, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)),
                     this, SLOT(filterChangedSlot()));
    filterChangedSlot();
}


/*
 * 函数名称：    exitSearchSlot()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.4.28
 * 函数功能：    退出搜索槽函数
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::exitSearchSlot() {
    isExitSearch = true;
}


/*
 * 函数名称：    gotoPageSlot()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.5.3
 * 函数功能：    跳转到指定页
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::gotoPageSlot() {
    QString text = ui->cbPage->currentText();
    int length = text.length();
    text = text.mid(1, length - 2);
    if (!stringIsNum(text)) {
        QMessageBox::information(this, "警告", "获取页索引失败！");
        return;
    }
    mCurPageIndex = text.toInt() - 1;
    if (mCurPageIndex < 0) {
        mCurPageIndex = 0;
    } else if (mCurPageIndex >= MAX_PAGE_NUM) {
        mCurPageIndex = MAX_PAGE_NUM - 1;
    }
    loadFile(mCurLogFilePath);
}


/*
 * 函数名称：    curContentChangedSlot()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.5.25
 * 函数功能：    日志内容区域当前选中项发生了改变
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::curContentChangedSlot() {
    logCurTime("当前项改变了");
    if (lwContent->count() > 0) {
        int count = lwContent->count();
        QListWidgetItem *item = lwContent->currentItem();
        if (item == NULL) {
            return;
        }
        QString text = item->text();
        while (text.contains("  ")) {
            text.replace("  ", " ");
        }
        QString level = text.split(" ").at(3);
        QColor color = mColorConfig->at(getLevelIndex(level));
        //将对应日志等级的文本设置成对应的颜色
        lwContent->setStyleSheet("QListWidget::Item:selected{background:#40ABABAB; color:" + color.name() + "}"
                                 "QListWidget::Item:hover{background:#20ABABAB;}");
    }
    logCurTime("结束");
}


/*
 * 函数名称：    selfVerticalScrollSlot(int value)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.3.10
 * 函数功能：    QListWidget自带的垂直滚动条值变化槽函数
 *             实现机制：通过控制自带的滚动条，来达到自定义滚动条控制显示内容的目的，
 *                      具体逻辑的实现较复杂，此处不做详细介绍；
 * 输入参数：    value:滚动条当前的值
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::selfVerticalScrollSlot(int value) {
    logCurTime("内部滑动：value=" + QString::number(value) + ", mLastScrollValue=" + QString::number(mLastScrollValue));
    int direction = 0;
    int step = value - mLastScrollValue;
    //与上一次的值作比较，判断滑动的方向，1表示向下滑动，-1表示向上滑动，0表示未滑动
    if (value > mLastScrollValue) {
        direction = 1;
    } else if (value < mLastScrollValue) {
        direction = -1;
    }
    //logCurTime("由" + QString::number(mLastScrollValue) + " >> " + QString::number(value));
    //如果是向下滑动
    if (direction == 1) {
        int first = mCurRange->getFirst();
        int visibleFirst = mCurRange->getVisibleFirst();
        int count = mCurRange->getCount();
        int pageItemNum = mCurRange->getPageItemNum();
        if ((visibleFirst != 0)
                && (mCurRange->getLast() != (mCurLines - 1))) {
            first += step;
        } else {
            mLastScrollValue = value;
        }
        visibleFirst += step;
        delete mCurRange;
        mCurRange = new ItemsRange(this, first, visibleFirst, count, pageItemNum, mCurLines);
        dispAreaData(mCurRange, direction);
    }
    //如果是向上滑动
    else if (direction == -1) {
        int first = mCurRange->getFirst();
        int visibleFirst = mCurRange->getVisibleFirst();
        int count = mCurRange->getCount();
        int pageItemNum = mCurRange->getPageItemNum();
        if ((first != 0)
                && (mCurRange->getLast() != (mCurLines - 1))) {
            first += step;
        } else if (mCurRange->getLast() == (mCurLines - 1)) {
            if ((visibleFirst + step) <= first) {
                first += step;
                if ((value == 0) && (first != 0)) {
                    mLastScrollValue = visibleFirst + step - first;
                }
            } else {
                mLastScrollValue = value;
            }
        } else {
            mLastScrollValue = value;
        }
        visibleFirst += step;
        delete mCurRange;
        mCurRange = new ItemsRange(this, first, visibleFirst, count, pageItemNum, mCurLines);
        dispAreaData(mCurRange, direction);
    }
    //如果未滑动
    else if (value == 0) {
        mLastScrollValue = 3;
        lwContent->verticalScrollBar()->setValue(1);
    }
}


/*
 * 函数名称：    verticalScrollSlot(int value)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.3.10
 * 函数功能：    自定义的垂直滚动条值变化槽函数
 * 输入参数：    value:滚动条当前的值
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::verticalScrollSlot(int value) {
    logCurTime("外部滑动：value=" + QString::number(value));
    int visibleFirst = value;
    int first = (visibleFirst > 0) ? (visibleFirst - 1) : visibleFirst;
    int count = mCurRange->getCount();
    int pageItemNum = mCurRange->getPageItemNum();
    delete mCurRange;
    mCurRange = new ItemsRange(this, first, visibleFirst, count, pageItemNum, mCurLines);
    dispAreaData(mCurRange, 0);
}





/*******************************************************************************************
 *                                   定时器槽函数区域
 * *****************************************************************************************/


/*
 * 函数名称：    startTimeoutSlot()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.5.4
 * 函数功能：    软件启动界面显示完后触发的槽函数
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::startTimeoutSlot() {
    startTimer->stop();
    //清空日志缓存文件（如果存在的话），防止上一次软件异常闪退导致缓存文件未删除的BUG
    clearLogCacheFile();
}


/*
 * 函数名称：    fileTimeoutSlot()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.16
 * 函数功能：    日志缓存文件大小监控定时器槽函数
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::fileTimeoutSlot() {
    fileTimer->stop();
    static bool hasFirstBreak = false;//是否第一次跳转过了
    static int cnt = 0;
    int size = logFileSize;
    QFile *file = new QFile(mLogCacheFilePath);
    if (file->exists()) {
        size = file->size();
    }
    file->close();
    delete file;
    cnt++;
    if (cnt >= 100) {
        cnt = 0;
    }
    //每210ms监测一次设备运行的进程
    if ((cnt % 21) == 0) {
        //是否启用了调试指定APP的功能，且ADB设备处于连接状态，且无事件在运行
        //if (isApplyDebugApp && mCurADBDeviceStatus && !mEventLoop->isRunning()) {
        if (isApplyDebugApp && mCurADBDeviceStatus && statusTimer->isActive()) {
            isCheckingPID = true;
            //查找该包名对应的进程号是否存在
            QStringList result = execWindowsCmd(ADBPath, "adb -s " + mCurADBDevice
                                                + " shell ps");
            QStringList threadList = result.at(0).split("\n");
            int num = threadList.size();
            QString temp1;
            QStringList temp2;
            QString oldAppPID;
            bool isFind = false;
            //逆序查找速度更快
            for (int i = 0; i < num; ++i) {
                temp1 = threadList.at(num - i - 1);
                if (temp1.contains(mCurDebugApp)) {
                    while (temp1.contains("  ")) {
                        temp1.replace("  ", " ");
                    }
                    temp2 = temp1.split(" ");
                    //共有9个信息栏
                    if (temp2.size() >= 9) {
                        oldAppPID = mCurDebugAppPID;
                        mCurDebugAppPID = temp2.at(1);
                        //logCurTime("进程号：" + mCurDebugAppPID);
                        //如果进程号发生了改变
                        if (oldAppPID != mCurDebugAppPID) {
                            hasFirstBreak = false;
                            if (mCurDebugAppPID != "") {
//                                curDebugAppLabel->setText("当前调试APP：" + mCurDebugApp
//                                                          + "(" + mCurDebugAppPID + ")");
                                QFontMetrics elidFont(curDebugAppLabel->font());
                                curDebugAppLabel->setText(elidFont.elidedText("当前调试APP：" + mCurDebugApp
                                                          + "(" + mCurDebugAppPID + ")",
                                                          Qt::ElideMiddle, curDebugAppLabel->maximumWidth()));
                            } else {
                                curDebugAppLabel->setText("");
                            }
                        }
                        //是否第一次跳转过
                        if (!hasFirstBreak) {
                            int index = ui->cbPID->findText(mCurDebugAppPID);
                            if (index != -1) {
                                ui->cbPID->setCurrentIndex(index);
                                hasFirstBreak = true;
                            }
                        }
                        //如果找到了则立即跳出循环，节省时间
                        isFind = true;
                        break;
                    }
                }
            }
            if (!isFind) {
                mCurDebugAppPID = "";
                curDebugAppLabel->setText("");
            }
            isCheckingPID = false;
        } else if (!isApplyDebugApp) {
            mCurDebugAppPID = "";
            curDebugAppLabel->setText("");
        }
    }
    //如果文件大小发生了变化，则重新加载该文件，以显示最新的文件内容，达到实时显示的目的
    if (logFileSize != size) {
        //logCurTime("logFileSize=" + QString::number(logFileSize) + ", size=" + QString::number(size));
        logFileSize = size;
        //记录更新文件内容前的相关显示参数
        int oldFilterRow = lwFilter->currentRow();
        int oldContentRow = lwContent->currentRow();
        int oldFilterScrollPosition = lwFilter->verticalScrollBar()->value();
        bool isMax = (ui->verticalScrollBar->value() == ui->verticalScrollBar->maximum()) ? true : false;
        oldFilterRow = (oldFilterRow > 0) ? oldFilterRow : 0;
        oldContentRow = (oldContentRow > 0) ? oldContentRow : 0;
        oldFilterScrollPosition = (oldFilterScrollPosition > 0) ? oldFilterScrollPosition : 0;
        QString oldFilterTag = (mCurFilters.size() > 0) ? mCurFilters.at(oldFilterRow) : "null";//获取旧Tag文本
        //更新文件内容
        updateFile(mLogCacheFilePath);
        //恢复更新文件前的相关显示参数
        if (oldFilterRow >= lwFilter->count()) {
            oldFilterRow = lwFilter->count() - 1;
            oldFilterRow = (oldFilterRow > 0) ? oldFilterRow : 0;
        } else {
            int size = mCurFilters.size();
            for (int i = 0; i < size; ++i) {
                if (mCurFilters.at(i) == oldFilterTag) {
                    oldFilterRow = i;
                    break;
                }
            }
        }
        QObject::disconnect(ui->lwFilter, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)),
                            this, SLOT(filterChangedSlot()));
        if (lwFilter->count() > 0) {
            if (oldFilterRow < lwFilter->count()) {
                lwFilter->setCurrentRow(oldFilterRow);
            } else {
                lwFilter->setCurrentRow(0);
            }
        }
        dispContainSearchString(ui->etSearch->text());
        QObject::connect(ui->lwFilter, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)),
                         this, SLOT(filterChangedSlot()));
        int max = lwFilter->verticalScrollBar()->maximum();
        if (oldFilterScrollPosition > max) {
            oldFilterScrollPosition = max;
        }
        lwFilter->verticalScrollBar()->setValue(oldFilterScrollPosition);
        //如果更新前显示的是最后一行，则表示实时显示最新内容；否则当前显示内容不变
        if (isMax) {
            ui->verticalScrollBar->setValue(ui->verticalScrollBar->maximum());
            if (lwContent->count() > 0) {
                isConnectScroll(false);
                lwContent->setCurrentRow(lwContent->count() - 1);
                isConnectScroll(true);
            }
        } else {
            if (oldContentRow >= lwContent->count()) {
                oldContentRow = lwContent->count() - 1;
                oldContentRow = (oldContentRow > 0) ? oldContentRow : 0;
            }
            if (lwContent->count() > 0) {
                isConnectScroll(false);
                lwContent->setCurrentRow(oldContentRow);
                isConnectScroll(true);
            }
        }
        //如果当前打开了Tag分析窗口，则实时更新数据
        if (mTagAnalyseDialog->isVisible() && (cnt >= 20)) {
            cnt = 0;
            QStringList tagList;
            QStringList numList;
            QString tag;
            QString num;
            QString text;
            QStringList temp;
            int size = lwFilter->count();
            int length;
            QListWidgetItem *item;
            for (int i = 1; i < size; ++i) {
                item = lwFilter->item(i);
                if (item == NULL) {
                    continue;
                }
                text = item->text();
                if (!text.contains("(") || !text.contains(")")) {
                    continue;
                }
                temp = text.split("(");
                length = temp.length();
                num = temp.at(length - 1).split(")").at(0);
                tag = text.remove("(" + num + ")");
                tagList.append(tag);
                numList.append(num);
            }
            mTagAnalyseDialog->updateDisp(tagList, numList);
        }
    }
    fileTimer->start(10);
}


/*
 * 函数名称：    statusTimeoutSlot()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.16
 * 函数功能：    ADB设备连接状态监控定时器槽函数
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::statusTimeoutSlot() {
    //如果正在检测系统进程PID，则不执行任何操作，避免造成冲突
    if (isCheckingPID) {
        return;
    }
    statusTimer->stop();
    QString info = "";
    //如果当前ADB设备连接成功了
    if (isADBConnectedSuccess(mCurADBDevice)) {
        if (mCurADBDevice == "") {
            return;
        }
        //如果上一次是未连接状态，且当前是实时终端模式，则表示是重连接，
        //需要通过手动模拟再次进入实时终端模式，以达到自动恢复显示的目的
        if (!mCurADBDeviceStatus && isTerminalMode) {
            isTerminalMode = false;
            clickTerminalAction();    //执行该函数之前mCurADBDeviceStatus必须为false
        }
        mCurADBDeviceStatus = true;
        info = "连接成功";
        ledOn(LED_ID_ADB_STATUS);
        isNeedBackupCacheFile = true;
        mReConnecCnt = 0;           //如果连接成功了，则清空重连计数器
    } else {
        mCurADBDeviceStatus = false;
        fileTimer->stop();
        //判断该设备是否还存在
        if ((mCurADBDevice != "") && !isExistDevice(mCurADBDevice)) {
            info = "设备丢失";
        } else {
            info = "连接断开";
        }
        ledOff(LED_ID_ADB_STATUS);
        mCurDebugAppPID = "";
        curDebugAppLabel->setText("");
        //如果非人为退出实时终端模式，且ADB设备由连接变为断开，则需要对文件进行一次备份操作
        if (!isPeopleExitTerminal && isNeedBackupCacheFile) {
            QFile *file = new QFile(mLogCacheFilePath);
            //如果日志缓存文件存在
            if (file->exists()) {
                //文件命名形如：xxx/xxx/cache/logCache-2.log
                QString path = mLogCacheFilePath.split(".").at(0) + "-"
                               + QString::number(mBackupCacheFilePath.size()) + ".log";
                QFile saveFile(path);
                //如果已经存在该文件，则先删除
                if (saveFile.exists()) {
                    saveFile.remove();
                }
                //开始备份文件
                if (file->copy(path)) {
                    mBackupCacheFilePath.append(path);
                }
            }
            file->close();
            delete file;
            isNeedBackupCacheFile = false;
        }
        //如果重连3次均失败，则退出ADB环境
        if (mReConnecCnt >= 3) {
            mReConnecCnt = 0;
            QMessageBox::information(this, "警告", "3次重连失败，即将退出ADB环境！");
            mCurADBDeviceStatus = false;    //更新当前ADB设备的连接状态
            statusTimer->stop();            //停止状态监控定时器
            mCurADBDevice = "";             //清空当前设备名
            adbDeviceLabel->clear();        //清空软件底部状态栏的显示
            //如果当前处于实时终端模式
            if (isTerminalMode) {
                prepareChangeModeParams(false); //退出实时终端模式
            }
        }
    }
    //更新软件底部状态栏的显示
    //adbDeviceLabel->setText("  【当前ADB设备】： " + mCurADBDeviceName + "  " + info + "  ");
    QFontMetrics elidFont(adbDeviceLabel->font());
    adbDeviceLabel->setText(elidFont.elidedText("  【当前ADB设备】： " + mCurADBDeviceName + "  " + info + "  ",
                            Qt::ElideMiddle, adbDeviceLabel->maximumWidth()));
    statusTimer->start(1000);
}


/*
 * 函数名称：    eventLoopTimeoutSlot()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.3.15
 * 函数功能：    主线程的事件循环控制定时器槽函数，仅执行一次
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::eventLoopTimeoutSlot() {
    mEventLoop->exit();         //退出事件阻塞，使主线程恢复执行
    eventLoopTimer->stop();     //关闭该定时器，保证单次执行即可
}





/*******************************************************************************************
 *                                    Action动作函数区域
 * *****************************************************************************************/

/*
 * 函数名称：    setingAction()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.4
 * 函数功能：    设置菜单点击动作
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::setingAction() {
    //创建设置对话框并显示
    Setting *dialog = new Setting;
    dialog->transParams(mCurFontType, mCurFontSize, mCurCodeType, ADBPath,
                        isApplyOfflineMode, isApplyRealtimeMode,
                        isFirstFilter, isSecondFilter, isApplyDebugApp, mCurDebugApp);
    dialog->exec();
    //如果点击了确定按钮，则设置属性生效
    if (dialog->getIsClickTrue()) {
        QStringList paramList;  //创建存储参数表
        bool isNeedUpdateFont = false;  //是否需要更新字体
        bool isNeedReloadFile = false;  //是否需要重载文件
        //判断当前字体类型是否有变化
        if (mCurFontType != dialog->getFontType()) {
            isNeedUpdateFont = true;
            mCurFontType = dialog->getFontType();
            paramList.append(mSettingParse->createParamsItem(FONT_TYPE, mCurFontType));
        }
        //判断当前字体大小是否有变化
        if (mCurFontSize != dialog->getFontSize()) {
            isNeedUpdateFont = true;
            mCurFontSize = dialog->getFontSize();
            paramList.append(mSettingParse->createParamsItem(FONT_SIZE, QString::number(mCurFontSize)));
        }
        //判断是否需要更新字体
        if (isNeedUpdateFont) {
            QFont font = QFont(mCurFontType, mCurFontSize, QFont::Normal);
            lwFilter->setFont(font);
            lwContent->setFont(font);
            labelTime->setFont(font);
            labelLevel->setFont(font);
            labelPID->setFont(font);
            labelTID->setFont(font);
            labelText->setFont(font);
            labelTag->setFont(font);
            autoAdjustTitleLabel();//自动调节标题标签的位置
        }
        //判断当前ADB路径是否有变化
        if (ADBPath != dialog->getADBPath()) {
            ADBPath = dialog->getADBPath();
            paramList.append(mSettingParse->createParamsItem(ADB_PATH, ADBPath));
        }
        //判断当前是否启用调试指定APP功能是否有变化
        if (isApplyDebugApp != dialog->getIsApplyDebugApp()) {
            isApplyDebugApp = dialog->getIsApplyDebugApp();
            paramList.append(mSettingParse->createParamsItem(IS_APPLY_DEBUG_APP,
                             (isApplyDebugApp) ? "true" : "false"));
        }
        if (isApplyDebugApp) {
            mCurDebugApp = dialog->getCurDebugApp();
            paramList.append(mSettingParse->createParamsItem(CUR_DEBUG_APP, mCurDebugApp));
        }
        //如果编码格式发生了变化则重新加载当前日志文件
        if (mCurCodeType != dialog->getCodeType()) {
            mCurCodeType = dialog->getCodeType();
            paramList.append(mSettingParse->createParamsItem(CODE_TYPE, mCurCodeType));
            //如果当前有文件打开，则需要重载文件
            if (!mCurLogFilePath.isEmpty()) {
                isNeedReloadFile = true;
            }
        }
        //如果系统过滤级别发生了变化
        if ((isFirstFilter != dialog->getIsFirstFilter())
                || (isSecondFilter != dialog->getIsSecondFilter())) {
            isFirstFilter = dialog->getIsFirstFilter();
            isSecondFilter = dialog->getIsSecondFilter();
            paramList.append(mSettingParse->createParamsItem(IS_FIRST_SYS_FILTER,
                             (isFirstFilter) ? "true" : "false"));
            paramList.append(mSettingParse->createParamsItem(IS_SECOND_SYS_FILTER,
                             (isSecondFilter) ? "true" : "false"));
            mFirstSysFilters = dialog->getFirstSysFilters();
            mSecondSysFilters = dialog->getSecondSysFilters();
            QString lint = SEPARATOR;
            QString temp = "";
            int size = mFirstSysFilters.size();
            for (int i = 0; i < size - 1; ++i) {
                temp += mFirstSysFilters.at(i) + lint;
            }
            if (size > 0) {
                temp += mFirstSysFilters.at(size - 1);
            }
            paramList.append(mSettingParse->createParamsItem(FIRST_SYS_FILTERS, temp));
            temp = "";
            size = mSecondSysFilters.size();
            for (int i = 0; i < size - 1; ++i) {
                temp += mSecondSysFilters.at(i) + lint;
            }
            if (size > 0) {
                temp += mSecondSysFilters.at(size - 1);
            }
            paramList.append(mSettingParse->createParamsItem(SECOND_SYS_FILTERS, temp));
            //如果当前有文件打开，则需要重载文件
            if (!mCurLogFilePath.isEmpty()) {
                isNeedReloadFile = true;
            }
        }
        //如果系统过滤应用模式发生了变化
        if ((isApplyOfflineMode != dialog->getIsApplyOfflineMode())
                || (isApplyRealtimeMode != dialog->getIsApplyRealtimeMode())) {
            isApplyOfflineMode = dialog->getIsApplyOfflineMode();
            isApplyRealtimeMode = dialog->getIsApplyRealtimeMode();
            paramList.append(mSettingParse->createParamsItem(IS_APPLY_OFFLINE_MODE,
                             (isApplyOfflineMode) ? "true" : "false"));
            paramList.append(mSettingParse->createParamsItem(IS_APPLY_REALTIME_MODE,
                             (isApplyRealtimeMode) ? "true" : "false"));
            //如果当前有文件打开，则需要重载文件
            if (!mCurLogFilePath.isEmpty()) {
                isNeedReloadFile = true;
            }
        }
        //如果需要重载文件
        if (isNeedReloadFile) {
            //如果当前是实时模式，则需要手动将该标志位置位，否则无法自动完整显示所有内容
            if (isTerminalMode) {
                mFilterChangedFlag = true;
            }
            refreshLogFileAction();
            //如果当前是实时终端模式，显示内容的最后一行，以便达到显示最新内容的目的
            if (isTerminalMode) {
                ui->verticalScrollBar->setValue(ui->verticalScrollBar->maximum());
                if (lwContent->count() > 0) {
                    lwContent->setCurrentRow(lwContent->count() - 1);
                }
            }
        }
        //将新的设置更新到设置文件中
        bool isSaveSuccess = mSettingParse->saveParamsToFile(mSettingFilePath, paramList);
        if (isSaveSuccess) {
            logCurTime("保存配置参数成功！");
        } else {
            logCurTime("保存配置参数失败！");
        }
    }
    delete dialog;
}


/*
 * 函数名称：    openFileAction()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.4
 * 函数功能：    打开文件菜单点击动作
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::openFileAction() {
    //打开后缀为*.txt的日志文件
    QString path = QFileDialog::getOpenFileName(this, "请选择LogCat文件",
                   QStandardPaths::writableLocation(QStandardPaths::DesktopLocation), "(*.txt *.log)");
    //logCurTime("path=" + path);
    //如果选择某一路径后点击了确定按钮
    if (!path.isEmpty()) {
        resetPageParams();
        loadFile(path); //加载该文件
    }
}


/*
 * 函数名称：    closeFileAction()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.4
 * 函数功能：    关闭文件菜单点击动作，释放相应的资源
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::closeFileAction() {
    ui->verticalScrollBar->setHidden(true);
    ui->etSearch->clear();
    //ui->cbLevel->setCurrentIndex(0);
    lwContent->clear();
    ui->swTimeFilter->setChecked(false);
    mCurLogFilePath = "";
    fileLabel->clear();
    clearAllDataList();
    resetPageParams();
}


/*
 * 函数名称：    aboutAction()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.6
 * 函数功能：    关于软件动作
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::aboutAction() {
    aboutSoftware *dialog = new aboutSoftware;
    dialog->exec();
    delete dialog;
}


/*
 * 函数名称：    refreshLogFileAction()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.9
 * 函数功能：    重载当前日志文件槽函数
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::refreshLogFileAction() {
    if (mCurLogFilePath.isEmpty()) {
        QMessageBox::information(this, "警告", "当前无文件打开，无法重载！");
        return;
    }
    resetPageParams();
    loadFile(mCurLogFilePath);
}


/*
 * 函数名称：    clickTerminalAction()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.16
 * 函数功能：    终端模式开关槽函数
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::clickTerminalAction() {
    //如果当前是实时终端模式
    if (isTerminalMode) {
        //如果确认退出实时终端模式
        if (QMessageBox::Ok == QMessageBox::information(this, "警告", "是否要退出终端模式？"
                , QMessageBox::Ok, QMessageBox::Cancel)) {
            isPeopleExitTerminal = true;
            //关闭日志缓存文件大小监控定时器
            if (fileTimer->isActive()) {
                logCurTime("fileTimer is Active");
                fileTimer->stop();
            }
            //关闭CMD进程
            if (cmdProcess != NULL) {
                logCurTime("!=NULL");
                if (cmdProcess->Running) {
                    logCurTime("Running");
                    cmdProcess->close();
                    cmdProcess->kill();
                    cmdProcess = NULL;
                    delete cmdProcess;
                }
            }
            prepareChangeModeParams(false); //退出实时终端模式
        }
    }
    //如果当前是离线查看模式
    else {
        //ADBPath = "D:/ruanjain/工具类/ADB调试";
        //如果设置了ADB工具路径
        if (ADBPath != "") {
            //当前是否连接了ADB设备
            if (mCurADBDevice != "") {
                //先判断日志缓存路径是否存在，若不存在则立即创建该路径
                if (!isDirExist(mLogCacheFilePath, true)) {
                    QMessageBox::information(this, "警告", "创建日志缓存文件失败，无法进入实时终端模式！");
                    return;
                }
                //创建CMD进程，用于将Android日志输出到指定文件
                cmdProcess = new QProcess(this);
                cmdProcess->setProcessChannelMode(QProcess::MergedChannels);
                cmdProcess->setWorkingDirectory(ADBPath);   //必须设置正确的进程工作路径，否则将找不到ADB工具
                //如果已经存在日志缓存文件，则获取其大小，否则该大小默认为0
                QFile *file = new QFile(mLogCacheFilePath);
                if (file->exists()) {
                    logFileSize = file->size();
                } else {
                    logFileSize = 0;
                }
                file->close();
                delete file;
                //清空日志输出的缓存
                QString cmd = "adb -s " + mCurADBDevice + " logcat -c";
                //此处if的目的是：当显示过程中ADB设备连接突然断开或丢失后再次重连时，不需要清空缓存
                if (mCurADBDeviceStatus) {
                    cmdProcess->start("cmd", QStringList() << "/c" << cmd);
                    cmdProcess->waitForStarted();
                    cmdProcess->waitForFinished();
                }
                //开始重新输出日志到指定文件
//                cmd = "adb -s " + mCurADBDevice + " logcat -b main -v time -s TableView>"
//                        + mLogCacheFilePath;
                cmd = "adb -s " + mCurADBDevice + " logcat -v time>"
                      + mLogCacheFilePath;
                cmdProcess->reset();
                cmdProcess->start("cmd", QStringList() << "/c" << cmd);
                prepareChangeModeParams(true); //进入实时终端模式
            } else {
                QMessageBox::information(this, "警告", "请先选择一个ADB设备！");
            }
        } else {
            QMessageBox::information(this, "警告", "未设置ADB工具路径，请检查！");
        }
    }
}


/*
 * 函数名称：    connectADBAction()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.16
 * 函数功能：    连接或断开ADB设备槽函数
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::connectADBAction() {
    //如果当前ADB设备是连接状态
    if (mCurADBDeviceStatus) {
        //如果确认断开当前ADB连接
        if (QMessageBox::Ok == QMessageBox::information(this, "警告", "是否要断开当前ADB连接？"
                , QMessageBox::Ok, QMessageBox::Cancel)) {
            isPeopleExitTerminal = true;
            //如果当前处于实时终端模式
            if (isTerminalMode) {
                prepareChangeModeParams(false); //退出实时终端模式
            }
            isNeedBackupCacheFile = false;
            mCurADBDeviceStatus = false;    //更新当前ADB设备的连接状态
            statusTimer->stop();            //停止状态监控定时器
            mCurADBDevice = "";             //清空当前设备名
            adbDeviceLabel->setText(NO_ADB_DEVICE);     //清空软件底部状态栏的显示
            ledOff(LED_ID_ADB_STATUS);
            statusTimeoutSlot();
        }
    }
    //如果当前ADB设备未连接，且未正在扫描ADB设备
    else if (!isScaningADBDevice) {
        isScaningADBDevice = true;
        //ADBPath = "D:/ruanjain/工具类/ADB调试";
        //如果设置了ADB工具路径
        if (ADBPath != "") {
            adbDeviceLabel->setText("  正在扫描ADB设备....  ");
            QStringList result = execWindowsCmd(ADBPath, "adb start-server"); //启动ADB服务器
            if (!result.at(0).isEmpty() && !result.at(0).contains("successfully")) {
                isScaningADBDevice = false;
                QMessageBox::information(this, "警告", "ADB路径错误，请重新设置！");
                return;
            }
            QStringList deviceNames = getCurOnlineDeviceNames();    //获取当前在线的ADB设备表
            //如果有设备存在
            if (deviceNames.size() != 0) {
                adbDeviceLabel->setText("  扫描完成，发现了" + QString::number(deviceNames.size()) + "个设备  ");
                //创建并显示设备选择对话框
                ConnectADB *dialog = new ConnectADB;
                dialog->transParams(deviceNames);
                dialog->exec(); //阻塞式，直到用户点击了按钮返回
                //如果点击了确定
                if (dialog->getIsClickTrue()) {
                    mCurADBDevice = dialog->getDeviceName();        //获取当前选中的设备串号
                    QString info = "";
                    if (mCurADBDevice == "") {
                        mCurADBDeviceStatus = false;
                        info = "无设备";
                    }
                    //判断该设备是否连接成功
                    if (isADBConnectedSuccess(mCurADBDevice)) {
                        statusTimer->start(1000);       //启动ADB设备连接状态监控定时器（1s监测一次）
                        mCurADBDeviceStatus = true;
                        info = "连接成功";
                        ledOn(LED_ID_ADB_STATUS);
                    } else {
                        mCurADBDeviceStatus = false;
                        info = "连接失败";
                    }
                    //更新软件底部的状态栏显示
                    QString name = "null";
                    QString manufacture = "";
                    QString type = "";
                    QString temp;
                    for (int i = 0; i < deviceNames.size(); ++i) {
                        temp = deviceNames.at(i);
                        if (temp.contains(mCurADBDevice)) {
                            if (temp.split(DEVICE_NAME_SEPERATE).size() == 1) {
                                name = temp;
                            } else if (temp.split(DEVICE_NAME_SEPERATE).size() == 5) {
                                manufacture = temp.split(DEVICE_NAME_SEPERATE).at(0);
                                type = temp.split(DEVICE_NAME_SEPERATE).at(1);
                                name = manufacture + "-" + type;
                            } else {
                                name = temp;
                            }
                            break;
                        }
                    }
                    if ((name == "") || (name == "-")) {
                        name = mCurADBDevice;
                    } else if (name.startsWith("-")) {
                        name += mCurADBDevice;
                    } else if (name.endsWith("-")) {
                        name = name.remove(0, 1) + "-" + mCurADBDevice;
                    }
                    mCurADBDeviceName = name;
                    //adbDeviceLabel->setText("  【当前ADB设备】： " + mCurADBDeviceName + "  " + info + "  ");
                    QFontMetrics elidFont(adbDeviceLabel->font());
                    adbDeviceLabel->setText(elidFont.elidedText("  【当前ADB设备】： " + mCurADBDeviceName + "  " + info + "  ",
                                            Qt::ElideMiddle, adbDeviceLabel->maximumWidth()));
                } else {
                    adbDeviceLabel->clear();
                }
                delete dialog;
            } else {
                QMessageBox::information(this, "警告", "未发现设备");
                adbDeviceLabel->setText(NO_ADB_DEVICE);
            }
        } else {
            QMessageBox::information(this, "警告", "请先设置ADB工具路径，再进行连接！");
        }
        isScaningADBDevice = false;
    }
}


/*
 * 函数名称：    gotoLineAction()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.3.16
 * 函数功能：    跳转到行
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::gotoLineAction() {
    if (mCurLogFilePath.isEmpty()) {
        QMessageBox::information(this, "警告", "当前无文件打开，无法进行行跳转！");
        return;
    }
    QVBoxLayout *vLayout = new QVBoxLayout;
    QHBoxLayout *hLayout1 = new QHBoxLayout;
    QHBoxLayout *hLayout2 = new QHBoxLayout;
    QHBoxLayout *hLayout3 = new QHBoxLayout;
    QLabel *label1 = new QLabel("当前行范围：1~" + QString::number(mCurLines));
    hLayout1->addWidget(label1);
    QLabel *label2 = new QLabel("跳转到行：");
    QLineEdit *edit = new QLineEdit;
    hLayout2->addWidget(label2);
    hLayout2->addWidget(edit);
    QPushButton *btnOK = new QPushButton("OK");
    QPushButton *btnCancel = new QPushButton("Cancel");
    hLayout3->addWidget(btnOK);
    hLayout3->addWidget(btnCancel);
    vLayout->addLayout(hLayout1);
    vLayout->addLayout(hLayout2);
    vLayout->addLayout(hLayout3);
    QDialog *dialog = new QDialog;
    dialog->setWindowTitle("跳转到行");
    dialog->setLayout(vLayout);
    dialog->adjustSize();                   //调整对话框尺寸，使其刚好容纳所有子控件
    dialog->setFixedSize(dialog->size());   //固定窗口大小
    QObject::connect(edit, SIGNAL(textChanged(QString)), this, SLOT(gotoLineEditTextChangedSLot(QString)));
    QObject::connect(btnCancel, SIGNAL(clicked()), dialog, SLOT(close()));
    QObject::connect(btnOK, SIGNAL(clicked()), this, SLOT(gotoLineSlot()));
    mCurOptDialog = dialog;
    dialog->exec();
    delete dialog;
}


/*
 * 函数名称：    registerSolftwareAction()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.3.16
 * 函数功能：    注册软件动作
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::registerSolftwareAction() {
    Security *security = Security::getInstance();
    if (security->isRegisted()) {
        QMessageBox::information(this, "提示", "软件已注册，注册码为：" + mRegisterNum);
    } else {
        //创建注册对话框并显示
        Register *dialog = new Register;
        dialog->exec();
        //如果点击了确定按钮且注册成功了
        if (dialog->getIsClickTrue()) {
            mRegisterNum = dialog->getRegisterNum();
            mSettingParse->saveRegisterNumToFile(mSettingFilePath, mRegisterNum);
        }
        delete dialog;
    }
}


/*
 * 函数名称：    backupSettingFileAction()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.4.19
 * 函数功能：    备份配置文件
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::backupSettingFileAction() {
    if (isTerminalMode) {
        QMessageBox::information(this, "警告", "无法在实时终端模式下备份，请先退出该模式！");
        return;
    }
    QString oldPath = mSettingFilePath;
    QString newPath = mSettingFilePath.split(SETTING_FILE_PATH).at(0) + BACKUP_SETTING_FILE_PATH;
    //备份原先的文件
    QString temp = mSettingFilePath.split(SETTING_FILE_PATH).at(0) + TEMP_SETTING_FILE_PATH;
    if (!QFile::copy(oldPath, temp)) {
        QMessageBox::information(this, "提示", "备份失败！");
        return;
    }
    //如果已经存在备份文件，则先删除
    QFile file(newPath);
    if (file.exists()) {
        file.remove();
    }
    if (QFile::copy(oldPath, newPath)) {
        QMessageBox::information(this, "提示", "备份成功！");
    } else {
        QFile::copy(temp, newPath); //若备份失败，则恢复原先的文件
        QMessageBox::information(this, "提示", "备份失败！");
    }
    //删除临时文件
    QFile tempFile(temp);
    if (tempFile.exists()) {
        tempFile.remove();
    }
}


/*
 * 函数名称：    importSettingFileAction()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.4.19
 * 函数功能：    导入配置文件
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::importSettingFileAction() {
    if (isTerminalMode) {
        QMessageBox::information(this, "警告", "无法在实时终端模式下导入，请先退出该模式！");
        return;
    }
    QString temp = SETTING_FILE_PATH;
    QString path = QFileDialog::getOpenFileName(this, "请选择要导入的配置文件",
                   mWindowsUserPath + "/" + temp.split("/").at(1), "(*.cfg)");
    if (!path.isEmpty()) {
        if (!mSettingParse->checkFile(path)) {
            QMessageBox::information(this, "警告", "该配置文件无法被软件识别，请检查配置文件格式是否符合规范！");
            return;
        }
        //备份原先的文件
        temp = mSettingFilePath.split(SETTING_FILE_PATH).at(0) + TEMP_SETTING_FILE_PATH;
        if (!QFile::copy(mSettingFilePath, temp)) {
            QMessageBox::information(this, "提示", "导入失败！");
            return;
        }
        //先删除原先的配置文件
        QFile file(mSettingFilePath);
        if (file.exists()) {
            file.remove();
        }
        if (QFile::copy(path, mSettingFilePath)) {
            //加载软件的设置文件，并设置相应的参数
            loadSoftwareSetting(mSettingFilePath);
            if (!mCurLogFilePath.isEmpty()) {
                QFont font = QFont(mCurFontType, mCurFontSize, QFont::Normal);
                lwFilter->setFont(font);
                lwContent->setFont(font);
                labelTime->setFont(font);
                labelLevel->setFont(font);
                labelPID->setFont(font);
                labelTID->setFont(font);
                labelText->setFont(font);
                labelTag->setFont(font);
                autoAdjustTitleLabel();//自动调节标题标签的位置
                if (!mCurLogFilePath.isEmpty()) {
                    refreshLogFileAction();
                }
            }
            QMessageBox::information(this, "提示", "导入成功！");
        } else {
            QFile::copy(temp, mSettingFilePath); //若导入失败，则恢复原先的文件
            QMessageBox::information(this, "提示", "导入失败！");
        }
        //删除临时文件
        QFile tempFile(temp);
        if (tempFile.exists()) {
            tempFile.remove();
        }
    }
}


/*
 * 函数名称：    nextPageAction()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.5.2
 * 函数功能：    跳转到下一页
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::nextPageAction() {
    mCurPageIndex++;
    if (mCurPageIndex >= MAX_PAGE_NUM) {
        mCurPageIndex = MAX_PAGE_NUM - 1;
    }
    loadFile(mCurLogFilePath);
}


/*
 * 函数名称：    lastPageAction()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.5.3
 * 函数功能：    跳转到上一页
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::lastPageAction() {
    mCurPageIndex--;
    if (mCurPageIndex < 0) {
        mCurPageIndex = 0;
    }
    loadFile(mCurLogFilePath);
}


/*
 * 函数名称：    pageOffsetAction()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.5.3
 * 函数功能：    页分界切换
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::pageOffsetAction() {
    bool temp = isPageOffsetMode;
    resetPageParams();
    isPageOffsetMode = !temp;
    loadFile(mCurLogFilePath);
}


/*
 * 函数名称：    openTagAnalyseWindowsAction()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.5.4
 * 函数功能：    打开Tag分析窗口
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::openTagAnalyseWindowsAction() {
    if (mCurLogFilePath.isEmpty()) {
        QMessageBox::information(this, "警告", "当前无日志文件打开，无法分析Tag！");
        return;
    }
    if (lwFilter->count() <= 0) {
        QMessageBox::information(this, "警告", "当前日志文件中无Tag，无法分析Tag！");
        return;
    }
    QListWidgetItem *item = lwFilter->item(0);
    if (item == NULL) {
        return;
    }
    if ((lwFilter->count() == 1) && (lwFilter->item(0)->text().contains(DEFAULT_TAG))) {
        QMessageBox::information(this, "警告", "当前日志文件中仅有All messages，无法分析Tag！");
        return;
    }
    QStringList tagList;
    QStringList numList;
    QString tag;
    QString num;
    QString text;
    QStringList temp;
    int size = lwFilter->count();
    int length;
    for (int i = 1; i < size; ++i) {
        item = lwFilter->item(i);
        if (item == NULL) {
            continue;
        }
        text = item->text();
        if (!text.contains("(") || !text.contains(")")) {
            continue;
        }
        temp = text.split("(");
        length = temp.length();
        num = temp.at(length - 1).split(")").at(0);
        tag = text.remove("(" + num + ")");
        tagList.append(tag);
        numList.append(num);
    }
    //创建设置对话框并显示
    delete mTagAnalyseDialog;
    mTagAnalyseDialog = new TagAnalyse;
    mTagAnalyseDialog->transParams(tagList, numList);
    mTagAnalyseDialog->show();
    mTagAnalyseDialog->exec();
}


/*
 * 函数名称：    restartSoftwareAction()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.5.8
 * 函数功能：    重启软件
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::restartSoftwareAction() {
    isClickRestart = true;
    qApp->closeAllWindows();
}


/*
 * 函数名称：    clearCurDispAction()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.5.8
 * 函数功能：    清空当前显示（实时模式下）
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::clearCurDispAction() {
    if (QMessageBox::Ok == QMessageBox::information(this, "警告",
            "是否清空当前显示？", QMessageBox::Ok, QMessageBox::Cancel)) {
        QString oldPID = ui->cbPID->currentText();
        clearAllDataList();//清空当前显示
        if (oldPID != "All") {
            QObject::disconnect(ui->cbPID, SIGNAL(currentIndexChanged(int)), this, SLOT(cbPIDChangedSlot()));
            ui->cbPID->addItem(oldPID);
            ui->cbPID->setCurrentIndex(1);
            QObject::connect(ui->cbPID, SIGNAL(currentIndexChanged(int)), this, SLOT(cbPIDChangedSlot()));
        }
    }
}


/*
 * 函数名称：    installAPKAction()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.6.5
 * 函数功能：    安装APK到指定设备
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::installAPKAction() {
    //如果当前ADB设备处于连接状态
    if (mCurADBDeviceStatus) {
        //打开后缀为*.apk的日志文件
        QString path = QFileDialog::getOpenFileName(this, "请选择APK文件",
                       QStandardPaths::writableLocation(QStandardPaths::DesktopLocation), "(*.apk)");
        if (!path.isEmpty()) {
            QString apkName = path.split("/").last();
            bool isInstallSuccess = false;
            int prePercent = 0;
            QByteArray backup;
            QString cmd = "adb -s " + mCurADBDevice + " install -r " + path;
            QString out;
            QString err;
            bool existProgress = false;
            QProcess process(0);
            process.setWorkingDirectory(ADBPath);
            process.start("cmd", QStringList() << "/c" << cmd);
            process.waitForFinished(500);
            backup = process.readAllStandardOutput();
            out = QString::fromLocal8Bit(backup);
            err = QString::fromLocal8Bit(process.readAllStandardError());
//            logCurTime("【out】=" + out);
//            logCurTime("【err】=" + err);
            if (out.startsWith("[") && out.contains("%]") && !out.contains("adb: error:")) {
                existProgress = true;
                QStringList list = out.split("\n");
                QString last;
                QString current;
                for (int i = 0; i < list.size(); ++i) {
                    if (list.at(i).startsWith("[") && list.at(i).contains("%]")) {
                        last = list.at(i);
                        current = last.mid(1, 4);
                        while (current.contains(" ")) {
                            current.replace(" ", "");
                        }
                        if (current.endsWith("%")) {
                            current = current.remove(current.length() - 1, 1);
                        } else {
                            current = "";
                        }
                    }
                }
                if (stringIsNum(current)) {
                    prePercent = current.toInt();
                } else {
                    prePercent = 0;
                }
            }
            if ((err == "") && !out.contains("adb: error:") && (prePercent != 100)) {
                //创建并显示加载进度条
                QProgressDialog *progressDialog = new QProgressDialog;
                progressDialog->setWindowFlags(Qt::FramelessWindowHint);
                progressDialog->setCancelButton(0);
                progressDialog->setLabelText("正在安装，请稍等....");
                progressDialog->setMinimum(0);
                progressDialog->setMaximum(100);
                progressDialog->setModal(true);
                progressDialog->setValue(prePercent);
                mainThreadWait_ms(10);  //阻塞主线程10ms，只有阻塞主线程，进度条才有机会显示出来
                QString current;                //当前已读取的大小
                QString oldPercent;             //记录上一次的百分比
                QString last;
                QStringList list;
                while (true) {
                    out = QString::fromUtf8(process.readAllStandardOutput());
                    err = QString::fromUtf8(process.readAllStandardError());
                    if (existProgress) {
                        list = out.split("\n");
                        for (int i = 0; i < list.size(); ++i) {
                            if (list.at(i).startsWith("[") && list.at(i).contains("%]")) {
                                last = list.at(i);
                                current = last.mid(1, 4);
                                while (current.contains(" ")) {
                                    current.replace(" ", "");
                                }
                                if (current.endsWith("%")) {
                                    current = current.remove(current.length() - 1, 1);
                                } else {
                                    current = "";
                                }
                            }
                        }
                        //logCurTime("【last】=" + last);
                        //logCurTime("【out】=" + out);
                        //logCurTime("【err】=" + err);
                        //更新文件加载的进度条
                        if (oldPercent != current && stringIsNum(current)) {
                            progressDialog->setValue(current.toInt());   //设置进度条
                            oldPercent = current;
                            mainThreadWait_ms(1);
                        }
                        if (last.contains("[100%]")) {
                            isInstallSuccess = true;
                            progressDialog->close();
                            mainThreadWait_ms(1);
                            delete progressDialog;
                            break;
                        }
                    } else {
                        if (out.contains("Success") || err.contains("Success")) {
                            isInstallSuccess = true;
                            progressDialog->setValue(100);
                            mainThreadWait_ms(100);
                            progressDialog->close();
                            mainThreadWait_ms(1);
                            delete progressDialog;
                            break;
                        } else {
                            //logCurTime("等待...");
                            progressDialog->setValue(50);
                            mainThreadWait_ms(1);
                        }
                    }
                    if (err != "") {
                        progressDialog->close();
                        mainThreadWait_ms(1);
                        delete progressDialog;
                        break;
                    }
                    mainThreadWait_ms(500);
//                    statusTimeoutSlot();
                    if (!mCurADBDeviceStatus) {
                        progressDialog->close();
                        mainThreadWait_ms(1);
                        delete progressDialog;
                        break;
                    }
                }
            }
            process.close();
            process.kill();
            if (isInstallSuccess || (prePercent == 100)) {
                QMessageBox::information(this, "提示", "应用【" + apkName + "】安装成功！");
            } else {
                if (!mCurADBDeviceStatus) {
                    QMessageBox::information(this, "提示", "应用【" + apkName + "】安装失败，原因：设备连接断开！");
                } else if (out.contains("adb: error:")) {
                    err = QString::fromUtf8(backup);
                    err = err.split("adb: error:").at(1);
                    QMessageBox::information(this, "提示", "应用【" + apkName + "】安装失败！\n\n【err】:"
                                             + "该设备的Android系统版本过低，不支持中文APK名;\n" + err.trimmed());
                } else {
                    QMessageBox::information(this, "提示", "应用【" + apkName + "】安装失败！\n\n【err】:" + err.trimmed());
                }
            }
        }
    } else {
        QMessageBox::information(this, "警告", "请先连接ADB设备！");
    }
}





/*******************************************************************************************
 *                                     私有函数区域
 * *****************************************************************************************/

/*
 * 函数名称：    closeEvent()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.16
 * 函数功能：    应用程序右上角X关闭触发事件
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::closeEvent(QCloseEvent *event) {
    //提醒用户是否退出软件，防止误操作使软件关闭而导致重要数据的丢失
    if (QMessageBox::Ok == QMessageBox::information(this, "警告",
            "是否退出软件？", QMessageBox::Ok, QMessageBox::Cancel)) {
        event->accept();    //接收该事件，即允许退出软件
    } else {
        event->ignore();    //忽略该事件，即不允许退出软件
        return;
    }
    //关闭ADB设备连接状态监控定时器
    if (statusTimer->isActive()) {
        statusTimer->stop();
    }
    //清空日志缓存文件（如果存在的话）
    clearLogCacheFile();
    //清空软件缓存目录（如果存在的话）
    clearCachePath();
    //保证在等待对话框关闭后再退出软件
    while (1) {
        mainThreadWait_ms(500);
        if (!mWaitDialog->isVisible()) {
            break;
        }
    }
    if (isClickRestart) {
        isClickRestart = false;
        QProcess::startDetached(qApp->applicationFilePath(), QStringList());
    }
}


/*
 * 函数名称：    clearLogCacheFile()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.16
 * 函数功能：    清空日志缓存文件（如果存在的话）
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::clearLogCacheFile() {
    killADBServer();    //立即关闭ADB服务
    rebuildLogCacheFile();  //如果存在日志备份文件，则对其进行文件重构
    QFile *file = new QFile(mLogCacheFilePath);
    //如果日志缓存文件存在
    if (file->exists()) {
        //此处代码目的：避免用户误点击导致日志缓存文件被删除，在删除前添加了确认对话框
        //用户是否先将缓存文件另存为
        while (1) {
            //如果需要将日志缓存文件另存为
            if (QMessageBox::Ok == QMessageBox::information(this, "警告",
                    "是否保存日志缓存文件？"
                    , QMessageBox::Ok, QMessageBox::Cancel)) {
                //设置要保存的路径和文件名
                QString fileName = QFileDialog::getSaveFileName(this, "保存文件",
                                   mLogCacheFilePath, "Text files (*.log)");
                //如果设置了另存为的路径
                if (!fileName.isEmpty()) {
                    QFile saveFile(fileName);
                    //如果已经存在该文件，则选择是否覆盖
                    if (saveFile.exists()) {
                        if (QMessageBox::Ok == QMessageBox::information(this, "警告",
                                "该文件已经存在，是否确定要覆盖？"
                                , QMessageBox::Ok, QMessageBox::Cancel)) {
                            saveFile.remove();
                            if (file->copy(fileName)) {
                                QMessageBox::information(this, "警告", "保存成功！");
                            } else {
                                QMessageBox::information(this, "警告", "保存失败！");
                            }
                            break;
                        }
                    } else {
                        if (file->copy(fileName)) {
                            QMessageBox::information(this, "警告", "保存成功！");
                        } else {
                            QMessageBox::information(this, "警告", "保存失败！");
                        }
                        break;
                    }
                } else {
                    QMessageBox::information(this, "警告", "另存为的文件名为空，请重新设置！");
                }
            }
            //如果不需要保存日志缓存文件
            else {
                //再次提醒用户是否真的不需要保存，因为一旦该文件被删除，
                //将无法再次回复，以免造成重要的调试信息因为误操作而丢失。
                if (QMessageBox::Ok == QMessageBox::information(this, "警告",
                        "缓存文件即将被删除，是否确认？"
                        , QMessageBox::Ok, QMessageBox::Cancel)) {
                    break;
                }
            }
        }
        //删除日志缓存文件
        file->close();
        file->remove();
        delete file;
    }
}


/*
 * 函数名称：    clearCachePath()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.4.19
 * 函数功能：    清空软件缓存目录（如果存在的话）
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::clearCachePath() {
    //如果软件缓存路径为空则删除缓存路径
    QString dirName = QCoreApplication::applicationDirPath() + "/cache";
    QDir dir(dirName);
    QFileInfoList infoList = dir.entryInfoList(QDir::Dirs | QDir::Files);
    int num = infoList.size();
    for (int i = 0; i < num; ++i) {
        if (infoList.at(i).isFile()) {
            QFile file(infoList.at(i).absoluteFilePath());
            file.remove();
        }
    }
    if (dir.entryInfoList(QDir::Dirs | QDir::Files).count() <= 2) {
        dir.rmdir(dirName);
    }
}


/*
 * 函数名称：    clearCachePath()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.4.19
 * 函数功能：    清空软件缓存目录（如果存在的话）
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::ledOn(int id) {
    isLedOn = true;
    switch (id) {
        case LED_ID_ADB_STATUS:
            adbStatusLabel->setStyleSheet("border-image: url(:/new/image/ledon.png);");
            break;
        default:
            isLedOn = false;
            break;
    }
}


/*
 * 函数名称：    clearCachePath()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.4.19
 * 函数功能：    清空软件缓存目录（如果存在的话）
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::ledOff(int id) {
    isLedOn = false;
    switch (id) {
        case LED_ID_ADB_STATUS:
            adbStatusLabel->setStyleSheet("border-image: url(:/new/image/ledoff.png);");
            break;
        default:
            break;
    }
}


/*
 * 函数名称：    updateCurFilters()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.4.21
 * 函数功能：    更新当前选中的PID号对应的信息
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::updateCurFilters() {
    long time = QDateTime::currentMSecsSinceEpoch();
    QString curPID = ui->cbPID->currentText();
    if (curPID == "All") {
        mCurFilters = mAllFilters;
        mCurFiltersNums = mAllFiltersNums;
    } else {
        int num;
        int cnt;
        QString tag;
        mCurFilters.clear();
        mCurFiltersNums.clear();
        for (int i = 0; i < mAllLines; ++i) {
            if (curPID == mAllPIDs.at(i)) {
                tag = mAllTags.at(i);
                if (!mCurFilters.contains(tag)) {
                    mCurFilters.append(tag);
                    mCurFiltersNums.append("0");
                }
                num = mCurFilters.size();
                for (int j = 0; j < num; ++j) {
                    if (tag == mCurFilters.at(j)) {
                        cnt = mCurFiltersNums.at(j).toInt();
                        mCurFiltersNums.replace(j, QString::number(cnt + 1));
                    }
                }
            }
        }
        QString lint = "#*$*#";
        num = mCurFilters.size();
        QStringList tempList;
        for (int i = 0; i < num; ++i) {
            tempList.append(mCurFilters.at(i) + lint + mCurFiltersNums.at(i));
        }
        tempList.sort(Qt::CaseInsensitive);
        //重构mCurFilters和mCurFiltersNums两个链表
        mCurFilters.clear();
        mCurFiltersNums.clear();
        QStringList tempSplit;
        for (int i = 0; i < num; ++i) {
            tempSplit = tempList.at(i).split(lint);
            mCurFilters.append(tempSplit.at(0));
            mCurFiltersNums.append(tempSplit.at(1));
        }
        int sum = 0;
        for (int i = 0; i < num; ++i) {
            sum += mCurFiltersNums.at(i).toInt();
        }
        mCurFilters.insert(0, DEFAULT_TAG);
        mCurFiltersNums.insert(0, QString::number(sum));
    }
    //计算每个TAG的数目并显示
    lwFilter->reset();
    lwFilter->clear();
    int filterNum = mCurFilters.size();
    QString text;
    for (int i = 0; i < filterNum; ++i) {
        if (curPID == "All") {
            text = mAllFilters.at(i) + "(" + mAllFiltersNums.at(i) + ")";
        } else {
            //text = dispList.at(i);
            text = mCurFilters.at(i) + "(" + mCurFiltersNums.at(i) + ")";
        }
        lwFilter->addItem(text);
        QListWidgetItem *item = lwFilter->item(lwFilter->count() - 1);
        if (item == NULL) {
            continue;
        }
        item->setToolTip(text);
    }
    time = QDateTime::currentMSecsSinceEpoch() - time;
    logCurTime("【进程切换耗时】" + QString::number(time) + "ms");
}


/*
 * 函数名称：    isBreakCurLine(QString text)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.4.28
 * 函数功能：    判断是否跳过解析当前行
 * 输入参数：    text:要判断的文本内容
 * 输出参数：    无
 * 返回值：      需要跳过返回true，否则返回false
*/
bool MainWindow::isBreakCurLine(QString text) {
    //如果当前为离线查看模式，且系统过滤器不应用于该模式，则不跳过
    if (!isTerminalMode && !isApplyOfflineMode) {
        return false;
    }
    //如果当前为实时终端模式，且系统过滤器不应用于该模式，则不跳过
    if (isTerminalMode && !isApplyRealtimeMode) {
        return false;
    }
    //如果一级过滤和二级过滤都没应用，则不跳过
    if (!isFirstFilter && !isSecondFilter) {
        return false;
    }
    //获取所有需要过滤的Tag
    QStringList filters;
    if (isFirstFilter) {
        for (int i = 0; i < mFirstSysFilters.size(); ++i) {
            filters.append(mFirstSysFilters.at(i));
        }
    }
    if (isSecondFilter) {
        for (int i = 0; i < mSecondSysFilters.size(); ++i) {
            filters.append(mSecondSysFilters.at(i));
        }
    }
    //判断当前行文本内容中是否包含需要过滤的Tag
    QString temp = text.split("): ").at(0);
    int size = filters.size();
    for (int i = 0; i < size; ++i) {
        if (temp.contains(filters.at(i))) {
            return true;
        }
    }
    return false;
}


/*
 * 函数名称：    setSearchPercentValue(int percent)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.4.28
 * 函数功能：    设置搜索进度百分比的值
 * 输入参数：    percent:要设置的百分比值
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::setSearchPercentValue(int percent) {
    if (!isSearching) {
        return;
    }
    if (percent < 0) {
        percent = 0;
    }
    if (percent > 100) {
        percent = 100;
    }
    mSearchDialog->setValue(percent);
    mainThreadWait_ms(1);
}


/*
 * 函数名称：    clearAllPageOffset
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.5.2
 * 函数功能：    清空所有页的页偏移值
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::clearAllPageOffset() {
    for (int i = 0; i < MAX_PAGE_NUM; ++i) {
        mAllPageOffset[i] = 0;
    }
}


/*
 * 函数名称：    resetPageParams()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.5.2
 * 函数功能：    重置页相关参数
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::resetPageParams() {
    mCurPageIndex = 0;
    mPageNum = 0;
    isPageOffsetMode = false;
    clearAllPageOffset();
    ui->actionLastPage->setEnabled(false);
    ui->actionNextPage->setEnabled(false);
    ui->actionPageOffset->setEnabled(false);
    //使页跳转控件隐藏
    ui->labelPage->setHidden(true);
    ui->cbPage->setHidden(true);
    QObject::disconnect(ui->cbPage, SIGNAL(currentIndexChanged(int)), this, SLOT(gotoPageSlot()));
    ui->cbPage->clear();
    QObject::connect(ui->cbPage, SIGNAL(currentIndexChanged(int)), this, SLOT(gotoPageSlot()));
}


/*
 * 函数名称：    getFileSeperatorNum(QTextStream in)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.5.3
 * 函数功能：    获取一个文件中换行符所占的字节数
 * 输入参数：    in:文件流
 * 输出参数：    无
 * 返回值：      返回文件中换行符所占的字节数
*/
int MainWindow::getFileSeperatorNum(QString path) {
    int num = 0;
    QString buff;
    QFile file(path);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        QMessageBox::information(this, "警告", "获取文件换行符占用字节数失败，文件可能不存在！");
        return num;
    }
    QTextStream in(&file);
    in.seek(0);
    while (!in.atEnd()) {
        buff = in.readLine();
        num = in.pos() - buff.toUtf8().length();
        break;
    }
    in.seek(0);
    file.close();
    num = (num >= 0) ? num : 0;
    return num;
}


/*
 * 函数名称：    resetChangeModeParams()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.5.3
 * 函数功能：    重置模式切换时的相关参数
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::resetChangeModeParams() {
    isCheckedUpdateFile = false;    //设置文件为规范性未检查
    mLastPos = 0;                   //设置上一次的文件光标位置为0
    closeFileAction();              //关闭加载的日志缓存文件
}


/*
 * 函数名称：    prepareChangeModeParams(bool isTerminal)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.5.3
 * 函数功能：    切换软件工作模式时的参数准备
 * 输入参数：    isTerminal:当前是否为实时终端模式
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::prepareChangeModeParams(bool isTerminal) {
    //进入实时终端模式
    if (isTerminal) {
        //失能部分控件
        ui->actionOpen->setEnabled(false);
        ui->actionRefresh->setEnabled(false);
        ui->history->setEnabled(false);
        ui->actionGotoLine->setEnabled(false);
        ui->actionClearCurDisp->setEnabled(true);
        ui->actionClose->setEnabled(false);
        //使页跳转控件隐藏
        ui->labelPage->setHidden(true);
        ui->cbPage->setHidden(true);
        QObject::disconnect(ui->cbPage, SIGNAL(currentIndexChanged(int)), this, SLOT(gotoPageSlot()));
        ui->cbPage->clear();
        QObject::connect(ui->cbPage, SIGNAL(currentIndexChanged(int)), this, SLOT(gotoPageSlot()));
        isTerminalMode = true; //更新当前工作模式状态
        ui->etSearch->setEnabled(false);
        resetChangeModeParams();    //重置模式切换时的相关参数
        //启动日志缓存文件大小监控定时器（10ms检测一次是否有变化）
        fileTimer->start(10);
        workModeLabel->setText(WORK_MODE_ONLINE);
        QObject::disconnect(ui->cbPID, SIGNAL(currentIndexChanged(int)), this, SLOT(cbPIDChangedSlot()));
    }
    //进入离线查看模式
    else {
        //使能部分控件
        ui->actionOpen->setEnabled(true);
        ui->actionRefresh->setEnabled(true);
        ui->history->setEnabled(true);
        ui->actionGotoLine->setEnabled(true);
        ui->actionClearCurDisp->setEnabled(false);
        ui->actionClose->setEnabled(true);
        isTerminalMode = false; //更新当前工作模式状态
        ui->etSearch->setEnabled(true);
        resetChangeModeParams();    //重置模式切换时的相关参数
        clearLogCacheFile();    //清空日志缓存文件
        isPeopleExitTerminal = false;
        workModeLabel->setText(WORK_MODE_OFFLINE);
        QObject::connect(ui->cbPID, SIGNAL(currentIndexChanged(int)), this, SLOT(cbPIDChangedSlot()));
    }
}


/*
 * 函数名称：    stringIsNum(QString str)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.5.3
 * 函数功能：    判断一个字符串是否可以转换为数字
 * 输入参数：    str:要判断的字符串
 * 输出参数：    无
 * 返回值：      可以转换返回true，否则返回false
*/
bool MainWindow::stringIsNum(QString str) {
    if (str.isEmpty()) {
        return false;
    }
    QChar temp;
    for (int i = 0; i < str.length(); ++i) {
        temp = str.at(i);
        if ((temp < '0') || (temp > '9')) {
            return false;
        }
    }
    return true;
}


/*
 * 函数名称：    rebuildLogCacheFile()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.5.3
 * 函数功能：    重构日志缓存文件
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::rebuildLogCacheFile() {
    QFile *file;
    int num = mBackupCacheFilePath.size();
    if (num > 0) {
        logCurTime("正在重构日志缓存文件......");
        //创建并显示重构进度条
        QProgressDialog *progressDialog = new QProgressDialog;
        progressDialog->setWindowFlags(Qt::FramelessWindowHint);
        progressDialog->setCancelButton(0);
        progressDialog->setLabelText("正在重构日志文件....");
        progressDialog->setMinimum(0);
        progressDialog->setMaximum(100);
        progressDialog->setValue(0);
        progressDialog->setModal(true);
        progressDialog->show();
        mainThreadWait_ms(10);  //阻塞主线程10ms，只有阻塞主线程，进度条才有机会显示出来
        int percent = 0;
        int oldPercent = 0;
        //构建日志备份文件的路径
        QString newPath = QCoreApplication::applicationDirPath() + LOG_REBUILD_FILE_PATH;
        QFile outFile(newPath);
        //如果已存在，则删除
        if (outFile.exists()) {
            outFile.remove();
        }
        //如果文件打开成功（若不存在则会自动创建，因为是只写的方式打开的）
        if (outFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
            //创建文件输出流
            QTextStream out(&outFile);
            out.setCodec(QTextCodec::codecForName(QString("UTF-8").toLatin1()));
            QString path;
            QString buff;
            QStringList logList;
            QString oldTime = "00-00 00:00:00.000";
            QString newTime;
            QString oldText = "";
            QStringList sameList;       //两个文件之间头尾相同的部分
            bool isHandleSame = false;  //是否处理了头尾相同的部分
            //如果存在本身的缓存文件，则参与分析重构
            file = new QFile(mLogCacheFilePath);
            if (file->exists()) {
                mBackupCacheFilePath.append(mLogCacheFilePath);
                num += 1;
            }
            delete file;
            int max[num];
            int sum = 0;
            int tempMax = 0;
            for (int i = 0; i < num; ++i) {
                tempMax = (int)((float)(900 * 1.0 / num));
                sum += tempMax;
                max[i] = tempMax;
            }
            tempMax = 0;
            max[num - 1] = max[num - 1] + 900 - sum;
            //开始分析重构
            for (int i = 0; i < num; ++i) {
                path = mBackupCacheFilePath.at(i);
                file = new QFile(path);
                //logCurTime("size[" + path + "]=" + QString::number(file->size()));
                //以只读的方式打开文件
                if (file->open(QIODevice::ReadOnly | QIODevice::Text)) {
                    //创建读取输入流
                    QTextStream in(file);
                    in.setCodec(QTextCodec::codecForName(QString("UTF-8").toLatin1()));
                    tempMax += (i > 0) ? max[i - 1] : 0;
                    long fileSize = file->size(); //文件总大小
                    long curSize = 0;
                    int seperatorNum = getFileSeperatorNum(path);
                    sameList.clear();
                    isHandleSame = false;
                    while (!in.atEnd()) {
                        buff = in.readLine();
                        curSize += buff.toUtf8().length() + seperatorNum;
                        //更新文件加载的进度条
                        percent = (int)(max[i] * (float)(curSize * 1.0 / fileSize)) + tempMax;
                        percent = (percent > (max[i] + tempMax)) ? (max[i] + tempMax) : percent;
                        percent /= 10;
                        if (oldPercent != percent) {
                            progressDialog->setValue(percent);   //设置进度条
                            oldPercent = percent;
                            mainThreadWait_ms(1);
                        }
                        if (checkLogCatFile(buff)) {
                            logList = textToList(reConstruction);
                            newTime = logList.at(0);
                            int result = QString::compare(newTime, oldTime);
                            //如果还未处理头尾相同部分，则必须先处理这部分（即过滤掉相同的部分）
                            if (!isHandleSame) {
                                if (result < 0) {
                                    continue;
                                } else if (result == 0) {
                                    sameList.append(buff);
                                    continue;
                                } else {
                                    for (int j = 0; j < sameList.size(); ++j) {
                                        if (!isHandleSame && (sameList.at(j) == oldText)) {
                                            isHandleSame = true;
                                            continue;
                                        } else if (isHandleSame) {
                                            out << sameList.at(j) << "\n";
                                            oldText = sameList.at(j);
                                        }
                                    }
                                    isHandleSame = true;
                                }
                            }
                            //如果新的时间大于旧的时间戳，或者等于且新旧文本不相同
                            if ((result > 0) || ((result == 0) && (oldText != buff))) {
                                out << buff << "\n";
                            }
                            oldTime = newTime;
                            oldText = buff;
                        }
                    }
                    //添加文件分隔符，便于分析观察，无实际用处
                    if (i != (num - 1)) {
                        QString seperator = "-------------------------文件重构分隔符--------------------------";
                        out << seperator << "\n";
                    }
                    file->close();//注意：此处文件必须关闭，否则后面将无法删除文件
                    delete file;
                } else {
                    QMessageBox::information(this, "警告", "重构文件失败，无法打开备份文件！");
                    progressDialog->close();
                    mainThreadWait_ms(10);
                    delete progressDialog;
                    return;
                }
            }
            outFile.close();//重构完毕，关闭输出流
            sum = 0;
            for (int i = 0; i < num; ++i) {
                tempMax = (int)((float)(100 * 1.0 / num));
                sum += tempMax;
                max[i] = tempMax;
            }
            max[num - 1] = max[num - 1] + 100 - sum;
            tempMax = 900;
            //删除备份文件和缓存文件（如果存在）
            for (int i = 0; i < num; ++i) {
                path = mBackupCacheFilePath.at(i);
                file = new QFile(path);
                file->remove();
                delete file;
                //更新文件加载的进度条
                tempMax += (i > 0) ? max[i - 1] : 0;
                percent = (int)(max[i] * (float)((i + 1) * 1.0 / num)) + tempMax;
                percent = (percent > (max[i] + tempMax)) ? (max[i] + tempMax) : percent;
                percent /= 10;
                if (oldPercent != percent) {
                    progressDialog->setValue(percent);   //设置进度条
                    oldPercent = percent;
                    mainThreadWait_ms(1);
                }
            }
            //将重构文件复制为缓存文件，然后删除重构文件
            if (outFile.copy(mLogCacheFilePath)) {
                mBackupCacheFilePath.clear();
                outFile.remove();
                progressDialog->setValue(100);
                mainThreadWait_ms(1);
            } else {
                while (1) {
                    //如果需要将日志缓存文件另存为
                    if (QMessageBox::Ok == QMessageBox::information(this, "警告",
                            "缓存文件被其他进程占用，无法复制，是否将重构文件复制到其他位置？"
                            , QMessageBox::Ok, QMessageBox::Cancel)) {
                        //设置要保存的路径和文件名
                        QString fileName = QFileDialog::getSaveFileName(this, "保存文件",
                                           newPath, "Text files (*.log)");
                        //如果设置了另存为的路径
                        if (!fileName.isEmpty()) {
                            QFile saveFile(fileName);
                            //如果已经存在该文件，则选择是否覆盖
                            if (saveFile.exists()) {
                                if (QMessageBox::Ok == QMessageBox::information(this, "警告",
                                        "该文件已经存在，是否确定要覆盖？"
                                        , QMessageBox::Ok, QMessageBox::Cancel)) {
                                    saveFile.remove();
                                    if (outFile.copy(fileName)) {
                                        QMessageBox::information(this, "警告", "保存成功！");
                                    } else {
                                        QMessageBox::information(this, "警告", "保存失败！");
                                    }
                                    mBackupCacheFilePath.clear();
                                    outFile.remove();
                                    break;
                                }
                            } else {
                                if (outFile.copy(fileName)) {
                                    QMessageBox::information(this, "警告", "保存成功！");
                                } else {
                                    QMessageBox::information(this, "警告", "保存失败！");
                                }
                                mBackupCacheFilePath.clear();
                                outFile.remove();
                                break;
                            }
                        } else {
                            QMessageBox::information(this, "警告", "另存为的文件名为空，请重新设置！");
                        }
                    }
                    //如果不需要保存日志重构文件
                    else {
                        //再次提醒用户是否真的不需要保存，因为一旦该文件被删除，
                        //将无法再次回复，以免造成重要的调试信息因为误操作而丢失。
                        if (QMessageBox::Ok == QMessageBox::information(this, "警告",
                                "重构文件即将被删除，是否确认？"
                                , QMessageBox::Ok, QMessageBox::Cancel)) {
                            mBackupCacheFilePath.clear();
                            outFile.remove();
                            break;
                        }
                    }
                }
            }
            logCurTime("重构完毕");
        } else {
            QMessageBox::information(this, "警告", "重构文件失败，无法创建重构文件！");
        }
        progressDialog->close();
        mainThreadWait_ms(10);
        delete progressDialog;
    }
}


/*
 * 函数名称：    killADBServer()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.5.5
 * 函数功能：    立即关闭ADB服务器
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::killADBServer() {
    QProcess process(0);
    process.setWorkingDirectory(ADBPath);
    process.start("cmd", QStringList() << "/c" << "adb kill-server");
    process.waitForStarted();
    process.waitForFinished(500);
    process.close();
    process.kill();
}


/*
 * 函数名称：    exitTerminalMode()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.6.26
 * 函数功能：    强制退出实时终端模式
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::exitTerminalMode() {
    isPeopleExitTerminal = true;
    //关闭日志缓存文件大小监控定时器
    if (fileTimer->isActive()) {
        logCurTime("fileTimer is Active");
        fileTimer->stop();
    }
    //关闭CMD进程
    if (cmdProcess != NULL) {
        logCurTime("!=NULL");
        if (cmdProcess->Running) {
            logCurTime("Running");
            cmdProcess->close();
            cmdProcess->kill();
            cmdProcess = NULL;
            delete cmdProcess;
        }
    }
    prepareChangeModeParams(false); //退出实时终端模式
}


/*
 * 函数名称：    execWindowsCmd(QString adbPath,QString cmd)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.16
 * 函数功能：    执行CMD命令
 * 输入参数：    adbPath:ADB工具路径     cmd:要执行的命令
 * 输出参数：    无
 * 返回值：      返回字符串列表，包含两个元素，其中[0]:表示执行结果返回； [1]:表示执行错误返回
*/
/*QStringList MainWindow::execWindowsCmd(QString adbPath,QString cmd)
{
    QStringList result;

    if (adbPath == "") {
        result.append("");
        result.append("未设置ADB路径");
        return result;
    }

    QProcess process(0);
    process.setWorkingDirectory(adbPath);
    process.start("cmd",QStringList()<<"/c"<<cmd);
    process.waitForStarted();
    process.waitForFinished(5000);

    QString out = QString::fromUtf8(process.readAllStandardOutput());
    QString err = QString::fromUtf8(process.readAllStandardError());

    process.close();
    process.kill();

    out = out.trimmed();
    err = err.trimmed();

    result.append(out);
    result.append(err);

    return result;
}*/


/*
 * 函数名称：    execWindowsCmd(QString adbPath,QString cmd)
 * 函数版本：        2.0.0
 * 作者：            HXL
 * 创建日期：    2017.3.13
 * 函数功能：    执行CMD命令，该版本为通过启动一个后台线程去执行，避免耗时等待阻塞UI线程
 * 输入参数：    adbPath:ADB工具路径     cmd:要执行的命令
 * 输出参数：    无
 * 返回值：      返回字符串列表，包含两个元素，其中[0]:表示执行结果返回； [1]:表示执行错误返回
*/
QStringList MainWindow::execWindowsCmd(QString adbPath, QString cmd) {
    QStringList result;
    if (mEventLoop->isRunning()) {
        logCurTime("已有事件在运行，命令【" + cmd + "】执行失败！");
    }
    if (!isStartADBServer) {
        isStartADBServer = true;
    }
    //创建执行ADB命令的线程，并建立相关的信号与槽的连接
    ADBThread *thread = new ADBThread(this);
    QObject::connect(thread, SIGNAL(done()), this, SLOT(threadDoneSlot()));
    QObject::connect(thread, SIGNAL(showDialog()), this, SLOT(showDialogSlot()));
    QObject::connect(thread, SIGNAL(closeDialog()), this, SLOT(closeDialogSlot()));
    thread->transParams(adbPath, cmd);  //必须先向该线程传入执行参数
    thread->start();                    //启动线程
    //mEventLoop->exec();                 //将主线程阻塞，以便等待ADB线程的命令执行完毕
    isExitEventLoop = false;
    while (!isExitEventLoop) {
        //QCoreApplication::processEvents(QEventLoop::AllEvents,10000);
        mainThreadWait_ms(20);
    }
    result = thread->getResult();       //获取ADB命令执行的结果
    delete thread;
    return result;
}


/*
 * 函数名称：    isADBConnectedSuccess(QString deviceName)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.16
 * 函数功能：    判断某个ADB设备是否连接成功
 * 输入参数：    deviceName:设备名
 * 输出参数：    无
 * 返回值：      连接成功返回true，否则返回false
*/
bool MainWindow::isADBConnectedSuccess(QString deviceName) {
    if ((deviceName == "") || !isExistDevice(deviceName)) {
        return false;
    }
    //判断方式：通过执行shell命令"pwd"打印当前路径，若返回"/"则表示连接成功
    QStringList result = execWindowsCmd(ADBPath, "adb -s " + deviceName + " shell pwd");
//    logCurTime("【out】" + result.at(0));
//    logCurTime("【err】" + result.at(1));
    if (result.at(1).startsWith(ADB_ERR_RESULT)) {
        return false;
    }
    if (result.at(0) == "/") {
        return true;
    } else {
        return false;
    }
}


/*
 * 函数名称：    getCurOnlineDeviceNames()
 * 函数版本：        1.0.0
 * 函数版本：        2.0.0  【增加ADB设备厂商、型号、系统版本以及SDK版本的获取显示功能】
 * 作者：            HXL
 * 创建日期：    2017.2.16
 * 函数功能：    获取当前在线的ADB设备
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      返回在线的设备名列表
*/
QStringList MainWindow::getCurOnlineDeviceNames() {
    QStringList result = execWindowsCmd(ADBPath, "adb devices");
    QString out = result.at(0);
    if (out.contains("List of devices attached")) {
        QStringList devices = out.split("\n");
        if (devices.size() > 1) {
            QStringList tempList;
            int size;
            QStringList deviceNames;
            QString info;
            QString serial;
            QString manufacturer;
            QString name;
            QString version;
            QString sdk;
            int parseStep;
            for (int i = 1; i < devices.size(); ++i) {
                manufacturer = "";
                name = "";
                version = "";
                sdk = "";
                serial = "";
                QString temp = devices.at(i);
                if (temp.contains("device") && (!temp.contains("List of devices attached"))) {
                    serial = temp.remove(temp.length() - 7, 7);
                    result = execWindowsCmd(ADBPath, "adb -s " + serial + " shell cat /system/build.prop");
                    if (!result.at(0).isEmpty()) {
                        tempList = result.at(0).split("\n");
                        size = tempList.size();
                        parseStep = 0;
                        for (int i = 0; i < size; ++i) {
                            temp = tempList.at(i);
                            if (temp.contains("ro.product.manufacturer=")) {
                                manufacturer = temp.split("=").at(1).trimmed();
                                manufacturer.replace("-", "_");
                                parseStep++;
                            } else if (temp.contains("ro.product.model=")) {
                                name = temp.split("=").at(1).trimmed();
                                name.replace("-", "_");
                                parseStep++;
                            } else if (temp.contains("ro.build.version.release=")) {
                                version = temp.split("=").at(1).trimmed();
                                version.replace("-", "_");
                                parseStep++;
                            } else if (temp.contains("ro.build.version.sdk=")) {
                                sdk = temp.split("=").at(1).trimmed();
                                sdk.replace("-", "_");
                                parseStep++;
                            }
                            if (parseStep >= 4) {
                                break;
                            }
                        }
                        info = manufacturer + DEVICE_NAME_SEPERATE + name + DEVICE_NAME_SEPERATE + version + DEVICE_NAME_SEPERATE + sdk + DEVICE_NAME_SEPERATE + serial;
                    } else {
                        info = serial;
                    }
                    deviceNames.append(info);
                }
            }
            if (deviceNames.size() > 0) {
                return deviceNames;
            }
        }
    }
    QStringList temp;
    return temp;
}


/*
 * 函数名称：    isExistDevice(QString deviceName)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.16
 * 函数功能：    判断某个ADB设备名是否存在
 * 输入参数：    deviceName:ADB设备名
 * 输出参数：    无
 * 返回值：      若存在则返回true，否则返回false
*/
bool MainWindow::isExistDevice(QString deviceName) {
    QStringList deviceNames = getCurOnlineDeviceNames();
    for (int i = 0; i < deviceNames.size(); ++i) {
        if (deviceNames.at(i).contains(deviceName)) {
            return true;
        }
    }
    return false;
}


/*
 * 函数名称：    logCurTime(QString text)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.3.10
 * 函数功能：    带时间戳的调试打印
 * 输入参数：    text:要打印的内容
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::logCurTime(QString text) {
    //如果调试开关打开了，则打印信息，否则不进行打印
#if DEBUG_SWITCH
    qDebug() << "【" + QDateTime::currentDateTime().toString("hh:mm:ss.zzz") + "】" + text;
#endif
}


/*
 * 函数名称：    updateCurRange()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.3.10
 * 函数功能：    更新当前显示缓冲区的范围
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::updateCurRange() {
    if (lwFilter->count() > 0) {
        int visibleHeight = lwContent->height();        //获取内容显示区的总高度
        int itemHeight = lwFilter->sizeHintForRow(0);   //获取一行所占的高度
        int visibleItemNum = visibleHeight / itemHeight;//计算可完整显示的行数
        if (visibleItemNum < 0) {
            visibleItemNum = 0;
        }
        //更新当前显示缓冲区范围
        int first = mCurRange->getFirst();
        int visibleFirst = mCurRange->getVisibleFirst();
        int count = visibleItemNum + visibleItemNum / 2;
        delete mCurRange;
        mCurRange = new ItemsRange(this, first, visibleFirst, count, visibleItemNum, mCurLevels.size());
//        logCurTime("mVisibleHeight=" + QString::number(visibleHeight));
//        logCurTime("mItemHeight=" + QString::number(itemHeight));
//        logCurTime("mVisibleItemNum=" + QString::number(mCurRange->getPageItemNum()));
//        logCurTime("first=" + QString::number(mCurRange->getFirst()));
//        logCurTime("visibleFirst=" + QString::number(mCurRange->getVisibleFirst()));
//        logCurTime("last=" + QString::number(mCurRange->getLast()));
    }
}


/*
 * 函数名称：    loadFile(QString path)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.9
 * 函数功能：    加载一个日志文件
 * 输入参数：    path:文件的绝对路径
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::loadFile(QString path) {
    //如果已经有文件正在加载中，则不执行任何操作
    if (isLoadingFile) {
        return;
    }
    isLoadingFile = true;
    mLogType = LOG_TYPE_UNKNOWN;
    logCurTime("开始加载文件....");
    //打开文件
    QFile logFile(path);
    if (!logFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
        QMessageBox::information(this, "警告", "读取文件失败，该文件可能不存在，请检查路径！");
        isLoadingFile = false;
        return;
    }
    //创建并显示加载进度条
    QProgressDialog *progressDialog = new QProgressDialog;
    progressDialog->setCancelButton(0);
    progressDialog->setLabelText("正在加载文件....");
    progressDialog->setMinimum(0);
    progressDialog->setMaximum(100);
    progressDialog->setValue(0);
    progressDialog->setModal(true);
    progressDialog->show();
    mainThreadWait_ms(10);  //阻塞主线程10ms，只有阻塞主线程，进度条才有机会显示出来
    long fileSize = logFile.size(); //文件总大小
    long curSize = 0;                //当前已读取的大小
    int oldPercent = 0;             //记录上一次的百分比
    int percent = 0;                //当前进度的百分比
    int curLines = 0;               //当前已解析并可显示的行数
    logCurTime("开始读取文件....");
    long time = QDateTime::currentMSecsSinceEpoch();
    long startTime = time;
    //开始读取文件内容
    QTextStream in(&logFile);
    in.setCodec(QTextCodec::codecForName(mCurCodeType.toLatin1()));
    QString buff;
    bool isChecked = false;
    if (!in.seek(mAllPageOffset[mCurPageIndex])) {
        QMessageBox::information(this, "警告", "文件寻址失败！");
        isLoadingFile = false;
        return;
    }
    int seperatorNum = getFileSeperatorNum(path);
    while (!in.atEnd()) {
        buff = in.readLine();
        curSize += buff.toUtf8().length() + seperatorNum;//更新当前已读取的大小
        //更新文件加载的进度条
        curLines = (isChecked == true) ? mAllTags.size() : 0;
        if (fileSize < MAX_CACHE_FILE_SIZE) {
            percent = (int)(100 * (float)(curSize * 1.0 / fileSize));
        } else {
            percent = (int)(100 * (float)(curLines * 1.0 / MAX_CACHE_LINES));
        }
        percent = (percent > 100) ? 100 : percent;
        if (oldPercent != percent) {
            if (!progressDialog->isVisible()) {
                closeFileAction();
                isLoadingFile = false;
                logCurTime("退出文件加载");
                return;
            }
            progressDialog->setValue(percent);   //设置进度条
            oldPercent = percent;
            mainThreadWait_ms(1);
        }
        //判断是否跳过解析改行内容，此处由系通过滤决定
        if (isBreakCurLine(buff)) {
            continue;
        }
        //如果还未对文件进行过规范性检查
        if (!isChecked) {
            //进行规范性检查，判断是否为Android的日志文件
            if (checkLogCatFile(buff)) {
                isChecked = true;
                mCurLogFilePath = path;
//                fileLabel->setText("  【当前日志文件路径】: " + mCurLogFilePath
//                                   + "  第" + QString::number(mCurPageIndex + 1) + "页"
//                                   + ((isPageOffsetMode == true) ? "(页分界偏移模式)" : "") + "  ");
                QFontMetrics elidFont(fileLabel->font());
                fileLabel->setText(elidFont.elidedText("  【当前日志文件路径】: " + mCurLogFilePath
                                                       + "  第" + QString::number(mCurPageIndex + 1) + "页"
                                                       + ((isPageOffsetMode == true) ? "(页分界偏移模式)" : "") + "  ",
                                                       Qt::ElideMiddle, fileLabel->maximumWidth()));
                clearAllDataList();                      //初始化相关变量
                //mFilters.append(DEFAULT_TAG);   //添加默认的过滤器
            } else {
                continue;
            }
        } else {
            if (!checkLogCatFile(buff)) {
                continue;
            }
        }
        //存储新的日志Tag
        QStringList logList = textToList(reConstruction);
        QString tag = logList.at(5);
        if (!mAllFilters.contains(tag)) {
            mAllFilters.append(tag);
        }
        //存储新的进程PID号
        QString pid = logList.at(2);
        if (!mPIDTypes.contains(pid)) {
            mPIDTypes.append(pid);
            ui->cbPID->addItem(pid);
        }
        //添加到全局数据表中
        addItemToDataList(logList);
        //判断是否为页分界偏移模式
        if (!isPageOffsetMode) {
            //如果已解析的行数达到了最大行数，且未到文件末尾
            if ((mAllTags.size() == MAX_CACHE_LINES) && (curSize != fileSize)) {
                if ((mCurPageIndex + 2) > mPageNum) {
                    mPageNum = mCurPageIndex + 2;
                    ui->actionPageOffset->setEnabled(true);
                    mAllPageOffset[mCurPageIndex + 1] = mAllPageOffset[mCurPageIndex] + curSize;
                    //使页跳转控件可见
                    ui->labelPage->setHidden(false);
                    ui->cbPage->setHidden(false);
                    QObject::disconnect(ui->cbPage, SIGNAL(currentIndexChanged(int)), this, SLOT(gotoPageSlot()));
                    if (ui->cbPage->count() == 0) {
                        ui->cbPage->addItem("第1页");
                    }
                    ui->cbPage->addItem("第" + QString::number(mPageNum) + "页");
                    QObject::connect(ui->cbPage, SIGNAL(currentIndexChanged(int)), this, SLOT(gotoPageSlot()));
                }
                break;
            }
        } else {
            //页分界偏移模式，即整体偏移量移动半页
            int tempLines = 0;
            if (mCurPageIndex == 0) {
                tempLines = MAX_CACHE_LINES / 2;
            } else {
                tempLines = MAX_CACHE_LINES;
            }
            if ((mAllTags.size() == tempLines) && (curSize != fileSize)) {
                if ((mCurPageIndex + 2) > mPageNum) {
                    mPageNum = mCurPageIndex + 2;
                    ui->actionPageOffset->setEnabled(true);
                    mAllPageOffset[mCurPageIndex + 1] = mAllPageOffset[mCurPageIndex] + curSize;
                    //使页跳转控件可见
                    ui->labelPage->setHidden(false);
                    ui->cbPage->setHidden(false);
                    QObject::disconnect(ui->cbPage, SIGNAL(currentIndexChanged(int)), this, SLOT(gotoPageSlot()));
                    if (ui->cbPage->count() == 0) {
                        ui->cbPage->addItem("第1页");
                    }
                    ui->cbPage->addItem("第" + QString::number(mPageNum) + "页");
                    QObject::connect(ui->cbPage, SIGNAL(currentIndexChanged(int)), this, SLOT(gotoPageSlot()));
                }
                break;
            }
        }
    }
    //控制下一页的按钮使能状态
    if (mCurPageIndex < (mPageNum - 1)) {
        ui->actionNextPage->setEnabled(true);
    } else {
        ui->actionNextPage->setEnabled(false);
    }
    //控制上一页的按钮使能状态
    if (mCurPageIndex > 0) {
        ui->actionLastPage->setEnabled(true);
    } else {
        ui->actionLastPage->setEnabled(false);
    }
    mAllLines = mAllTags.size();
    progressDialog->setValue(100);
    mainThreadWait_ms(1);
    //对数据解析后识别出来的所有Tag进行重新排序
    if ((mAllFilters.size() > 0) && (mAllFilters.at(0).contains(DEFAULT_TAG))) {
        mAllFilters.removeAt(0);
    }
    mAllFilters.sort(Qt::CaseInsensitive);
    mAllFilters.insert(0, DEFAULT_TAG);
    logCurTime("读取文件完毕");
    time = QDateTime::currentMSecsSinceEpoch() - time;
    logCurTime("【读取文件耗时】" + QString::number(time) + "ms");
    logFile.close();
    logCurTime("关闭文件");
    if (!isChecked) {
        QMessageBox::information(this, "警告", "解析LogCat文件失败，此文件可能不是LogCat文件！");
        progressDialog->close();
        isLoadingFile = false;
        delete progressDialog;
        return;
    }
    logCurTime("开始构建TAG....");
    time = QDateTime::currentMSecsSinceEpoch();
    //计算每个TAG的数目并显示
    mCurFilters = mAllFilters;
    int filterNum = mCurFilters.size();
    int tagNum = mAllLines;
    int count = 0;
    QString text;
    QString temp;
    text = DEFAULT_TAG;
    mAllFiltersNums.clear();
    mAllFiltersNums.append(QString::number(tagNum));
    text += "(" + QString::number(tagNum) + ")";
    lwFilter->addItem(text);
    QListWidgetItem *item = lwFilter->item(lwFilter->count() - 1);
    if (item == NULL) {
        QMessageBox::information(this, "警告", "解析LogCat文件失败，无法获取Filter栏指定对象！");
        progressDialog->close();
        isLoadingFile = false;
        delete progressDialog;
        return;
    }
    item->setToolTip(text);
    for (int i = 1; i < filterNum; ++i) {
        count = 0;
        temp = mCurFilters.at(i);
        for (int j = 0; j < tagNum; ++j) {
            if (temp == mAllTags.at(j)) {
                count++;
            }
        }
        mAllFiltersNums.append(QString::number(count));
        text = temp + "(" + QString::number(count) + ")";
        lwFilter->addItem(text);
        item = lwFilter->item(lwFilter->count() - 1);
        if (item == NULL) {
            QMessageBox::information(this, "警告", "解析LogCat文件失败，无法获取Filter栏指定对象！");
            progressDialog->close();
            isLoadingFile = false;
            delete progressDialog;
            return;
        }
        item->setToolTip(text);
    }
    logCurTime("构建TAG完毕");
    time = QDateTime::currentMSecsSinceEpoch() - time;
    logCurTime("【构建TAG耗时】" + QString::number(time) + "ms");
    ui->swTimeFilter->setChecked(false);//加载文件后，设置时间过滤器为失能状态
    //最后默认选中第一行，并显示所有日志内容
    time = QDateTime::currentMSecsSinceEpoch();
    QObject::disconnect(ui->lwFilter, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)),
                        this, SLOT(filterChangedSlot()));
    if (lwFilter->count() > 0) {
        lwFilter->setCurrentRow(0);
    }
    updateCurRange();
    QObject::connect(ui->lwFilter, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)),
                     this, SLOT(filterChangedSlot()));
    time = QDateTime::currentMSecsSinceEpoch() - time;
    logCurTime("【设置当前行耗时】" + QString::number(time) + "ms");
    logCurTime("开始显示日志....");
    time = QDateTime::currentMSecsSinceEpoch();
    dispContainSearchString(ui->etSearch->text());
    logCurTime("显示完毕");
    time = QDateTime::currentMSecsSinceEpoch() - time;
    logCurTime("【显示日志耗时】" + QString::number(time) + "ms");
    startTime = QDateTime::currentMSecsSinceEpoch() - startTime;
    logCurTime("【****************加载一次显示耗时】" + QString::number(startTime) + "ms");
    //将打开的文件路径添加到历史记录中
    addHistoryAction(path);
    //设置页跳转控件的当前选项
    QObject::disconnect(ui->cbPage, SIGNAL(currentIndexChanged(int)), this, SLOT(gotoPageSlot()));
    ui->cbPage->setCurrentIndex(mCurPageIndex);
    QObject::connect(ui->cbPage, SIGNAL(currentIndexChanged(int)), this, SLOT(gotoPageSlot()));
    //清空正在加载文件的标志位
    isLoadingFile = false;
    //关闭加载进度框
    progressDialog->close();
    mainThreadWait_ms(10);
    delete progressDialog;
}


/*
 * 函数名称：    updateFile(QString path)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.3.10
 * 函数功能：    更新一个日志文件的内容
 * 输入参数：    path:文件路径
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::updateFile(QString path) {
    logCurTime("开始加载文件....");
    QFile logFile(path);
    if (!logFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
        QMessageBox::information(this, "警告", "读取文件失败！");
        exitTerminalMode();
        return;
    }
    logCurTime("开始读取文件....");
    long time = QDateTime::currentMSecsSinceEpoch();
    long startTime = time;
    //开始读取文件内容
    QTextStream in(&logFile);
    in.setCodec(QTextCodec::codecForName(mCurCodeType.toLatin1()));
    QString buff;
    //计算上一次已经加载的光标位置
    long lastPos = mLastPos;
    if (!in.seek(lastPos)) {
        QMessageBox::information(this, "警告", "文件寻址失败！");
        exitTerminalMode();
        return;
    }
    //在文件加载前，创建Tag(num)链表
    QString lint = "#*$*#";
    int size = mAllFilters.size();
    if (size != mAllFiltersNums.size()) {
        exitTerminalMode();
        return;
    }
    QStringList tempList;
    for (int i = 0; i < size; ++i) {
        tempList.append(mAllFilters.at(i) + lint + mAllFiltersNums.at(i));
    }
    int seperatorNum = getFileSeperatorNum(path);
    while (!in.atEnd()) {
        buff = in.readLine();
        mLastPos += buff.toUtf8().length() + seperatorNum;
        //判断是否跳过解析改行内容，此处由系通过滤决定
        if (isBreakCurLine(buff)) {
            continue;
        }
        //如果还未对文件进行过规范性检查
        if (!isCheckedUpdateFile) {
            //进行规范性检查，判断是否为Android的日志文件
            if (checkLogCatFile(buff)) {
                isCheckedUpdateFile = true;
                mCurLogFilePath = path;
                //fileLabel->setText("  【当前日志文件路径】: " + mCurLogFilePath + "  ");
                fileLabel->setText("");
//                QFontMetrics elidFont(fileLabel->font());
//                fileLabel->setText(elidFont.elidedText("  【当前日志文件路径】: " + mCurLogFilePath
//                                                            + "  第" + QString::number(mCurPageIndex + 1) + "页"
//                                                            + ((isPageOffsetMode == true) ? "(页分界偏移模式)" : "") + "  ",
//                                                            Qt::ElideMiddle,fileLabel->maximumWidth()));
                clearAllDataList();                      //初始化相关变量
                //mAllFilters.append(DEFAULT_TAG);   //添加默认的过滤器
            } else {
                continue;
            }
        } else {
            if (!checkLogCatFile(buff)) {
                continue;
            }
        }
        //存储新的日志Tag
        QStringList logList = textToList(reConstruction);
        QString tag = logList.at(3);
        if (!mAllFilters.contains(tag)) {
            mAllFilters.append(tag);
        }
        //存储新的进程PID号
        QString pid = logList.at(2);
        if (!mPIDTypes.contains(pid)) {
            mPIDTypes.append(pid);
            ui->cbPID->addItem(pid);
        }
        //添加到全局数据表中
        addItemToDataList(logList);
        //如果已加载的行数达到了最大行数，则清空全局数据缓冲区，避免时间长久造成的大量内存占用
        if (mAllTags.size() == MAX_TERNIMAL_CACHE_LINES) {
            closeFileAction();
            return;
        }
    }
    logCurTime("读取文件完毕");
    time = QDateTime::currentMSecsSinceEpoch() - time;
    logCurTime("【读取文件耗时】" + QString::number(time) + "ms");
    logFile.close();
    logCurTime("关闭文件");
    //更新显示时如果检查不合格，不需要弹出提示框
//    if (!isCheckedUpdateFile) {
//        QMessageBox::information(this,"警告","解析LogCat文件失败，此文件可能不是LogCat文件！");
//        return;
//    }
    logCurTime("开始构建TAG....");
    time = QDateTime::currentMSecsSinceEpoch();
    //根据新增的内容，更新每个Tag的当前数量
    int oldAllLines = mAllLines;
    mAllLines = mAllTags.size();
    mAddNewLines = mAllLines - oldAllLines;
    QString tempTag;
    QStringList tempSplit;
    int tempNum;
    int tempIndex;
    for (int i = 0; i < mAddNewLines; ++i) {
        tempTag = mAllTags.at(oldAllLines + i);
        tempNum = 0;
        for (int j = 0; j < tempList.size(); ++j) {
            tempNum = 0;
            tempSplit = tempList.at(j).split(lint);
            if (tempTag == tempSplit.at(0)) {
                tempIndex = j;
                tempNum = tempSplit.at(1).toInt() + 1;
                break;
            }
        }
        if (tempNum == 0) {
            tempList.append(tempTag + lint + QString::number(1));
        } else {
            tempList.replace(tempIndex, tempTag + lint + QString::number(tempNum));
        }
    }
    //对数据解析后识别出来的所有Tag进行重新排序
    if ((tempList.size() > 0) && (tempList.at(0).contains(DEFAULT_TAG))) {
        tempList.removeAt(0);
    }
    tempList.sort(Qt::CaseInsensitive);
    tempList.insert(0, DEFAULT_TAG + lint + QString::number(mAllLines));
    size = mAllFilters.size();
    //判断解析器识别出来的总Tag数与更新后重构Tag(num)链表的数目是否相等
    //若不相等，则表示数据更新错误
    if (size > 0) {
        if (mAllFilters.at(0).contains(DEFAULT_TAG)) {
            if (size != tempList.size()) {
                QMessageBox::information(this, "警告", "Tag构建失败！");
                exitTerminalMode();
                return;
            }
        } else {
            if ((size + 1) != tempList.size()) {
                QMessageBox::information(this, "警告", "Tag构建失败！");
                exitTerminalMode();
                return;
            }
        }
    }
    //重构mAllFilters和mAllFiltersNums两个链表
    mAllFilters.clear();
    mAllFiltersNums.clear();
    for (int i = 0; i < size; ++i) {
        tempSplit = tempList.at(i).split(lint);
        mAllFilters.append(tempSplit.at(0));
        mAllFiltersNums.append(tempSplit.at(1));
    }
    //将新的Tag种类及对应的数目显示到Filters栏
    lwFilter->reset();
    lwFilter->clear();
    mCurFilters = mAllFilters;
    int filterNum = mCurFilters.size();
    QString text;
    for (int i = 0; i < filterNum; ++i) {
        text = mCurFilters.at(i) + "(" + mAllFiltersNums.at(i) + ")";
        lwFilter->addItem(text);
    }
    //进程PID号筛选
    updateCurFilters();
    logCurTime("构建TAG完毕");
    time = QDateTime::currentMSecsSinceEpoch() - time;
    logCurTime("【构建TAG耗时】" + QString::number(time) + "ms");
    ui->swTimeFilter->setChecked(false);//加载文件后，设置时间过滤器为失能状态
    //最后默认选中第一行，并显示所有日志内容
    time = QDateTime::currentMSecsSinceEpoch();
    QObject::disconnect(ui->lwFilter, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)),
                        this, SLOT(filterChangedSlot()));
    if (lwFilter->count() > 0) {
        lwFilter->setCurrentRow(0);
    }
    //updateCurRange();
    QObject::connect(ui->lwFilter, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)),
                     this, SLOT(filterChangedSlot()));
    time = QDateTime::currentMSecsSinceEpoch() - time;
    logCurTime("【设置当前行耗时】" + QString::number(time) + "ms");
//    logCurTime("开始显示日志....");
//    time = QDateTime::currentMSecsSinceEpoch();
//    dispContainSearchString(ui->etSearch->text());
//    logCurTime("显示完毕");
//    time = QDateTime::currentMSecsSinceEpoch() - time;
//    logCurTime("【显示日志耗时】" + QString::number(time) + "ms");
    startTime = QDateTime::currentMSecsSinceEpoch() - startTime;
    logCurTime("【****************加载一次显示耗时】" + QString::number(startTime) + "ms");
}


/*
 * 函数名称：    isTimeString(QString str)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.9
 * 函数功能：    判断一个字符串是否为时间字符串
 * 输入参数：    str:要判断的字符串
 * 输出参数：    无
 * 返回值：      是的返回true，否则返回false
*/
bool MainWindow::isTimeString(QString str) {
    //时间字符串的格式为："01-01 00:00:00.000"
    QStringList tempStrList = str.split(" ");
    if (tempStrList.length() != 2) {
        return false;
    }
    if (!tempStrList[0].contains("-") || (tempStrList[0].length() != 5)) {
        return false;
    }
    tempStrList = tempStrList[1].split(":");
    if (tempStrList.length() != 3) {
        return false;
    }
    if (!tempStrList[2].contains(".") || (tempStrList[2].length() != 6)) {
        return false;
    }
    return true;
}


/*
 * 函数名称：    updateTimeStringToWidget(QString startTime, QString stopTime)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.9
 * 函数功能：    将起止时间更新到控件显示
 * 输入参数：    startTime:起始时间  stopTime:终止时间
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::updateTimeStringToWidget(QString startTime, QString stopTime) {
    if (isTimeString(startTime) && isTimeString(stopTime)) {
        QStringList tempList = startTime.split(" ");
        QStringList tempList1 = tempList[0].split("-");
        QStringList tempList2 = tempList[1].split(":");
        QStringList tempList3 = tempList2[2].split(".");
        QString startMonth = tempList1[0];
        QString startDay = tempList1[1];
        QString startHour = tempList2[0];
        QString startMinute = tempList2[1];
        QString startSecond = tempList3[0];
        tempList = stopTime.split(" ");
        tempList1 = tempList[0].split("-");
        tempList2 = tempList[1].split(":");
        tempList3 = tempList2[2].split(".");
        QString stopMonth = tempList1[0];
        QString stopDay = tempList1[1];
        QString stopHour = tempList2[0];
        QString stopMinute = tempList2[1];
        QString stopSecond = tempList3[0];
        //如果毫秒数不为0，则将时间秒加一
        if (QString::compare(tempList3[1], "000") != 0) {
            QDateTime date = QDateTime::fromString(stopTime.split(".")[0], "MM-dd hh:mm:ss");
            QString tempStr = date.addSecs(1).toString("MM-dd hh:mm:ss");
            stopSecond = tempStr.split(":")[2];
        }
        isConnectTimeFilter(false);  //断开时间过滤器的信号与槽的连接
        ui->cbStartMonth->setCurrentIndex(ui->cbStartMonth->findText(startMonth));
        ui->cbStartDay->setCurrentIndex(ui->cbStartDay->findText(startDay));
        ui->cbStartHour->setCurrentIndex(ui->cbStartHour->findText(startHour));
        ui->cbStartMinute->setCurrentIndex(ui->cbStartMinute->findText(startMinute));
        ui->cbStartSecond->setCurrentIndex(ui->cbStartSecond->findText(startSecond));
        ui->cbStopMonth->setCurrentIndex(ui->cbStopMonth->findText(stopMonth));
        ui->cbStopDay->setCurrentIndex(ui->cbStopDay->findText(stopDay));
        ui->cbStopHour->setCurrentIndex(ui->cbStopHour->findText(stopHour));
        ui->cbStopMinute->setCurrentIndex(ui->cbStopMinute->findText(stopMinute));
        ui->cbStopSecond->setCurrentIndex(ui->cbStopSecond->findText(stopSecond));
        isConnectTimeFilter(true);  //建立时间过滤器的信号与槽的连接
    }
}


/*
 * 函数名称：    dispContainSearchString(QString str)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.4
 * 函数功能：    显示包含搜索栏信息的所有日志信息
 * 输入参数：    str:要搜索的字符串信息
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::dispContainSearchString(QString str) {
    if (mAllLines <= 0) {
        return;
    }
    //加载大于等于某一日志等级且指定Tag的所有日志信息
    QModelIndexList rows = ui->lwFilter->selectionModel()->selectedRows();
//    ui->lwFilter->selectionModel()->selectedIndexes();
//    ui->lwFilter->selectedIndexes();
    QStringList tagList;
    qDebug() << "=======================";
    for (QModelIndex row : rows) {
        qDebug() << "row======" << row.row() << "    " << ui->lwFilter->currentRow();
        tagList.append(mCurFilters.at(row.row()));
    }
    tagList.append(mCurFilters.at(ui->lwFilter->currentRow()));
    for (QString tmp : tagList) {
        qDebug() << "selected:" << tmp;
    }
    loadLevelMessage(ui->cbLevel->currentText(), tagList);
//        loadLevelMessage(ui->cbLevel->currentText(), mCurFilters.at(ui->lwFilter->currentRow()));
    int percentMax = 0;
    int prePercent = 40;//用于搜索进程，前面已经进行的占比40%
    int cnt = 0;
    int total = mCurTexts.size();
    int oldPercent = 0;
    if (ui->swTimeFilter->isChecked()) {
        //用于搜索进程，该处占比30%
        percentMax = 30;
        cnt = 0;
        for (int i = 0; i < mCurTexts.size(); ++i) {
            //将不属于起止时间范围的日志从当前数据表中移除
            if (!isContainTime(mCurTimes.at(i))) {
                mCurTimes.removeAt(i);
                mCurLevels.removeAt(i);
                mCurPIDs.removeAt(i);
                mCurTIDs.removeAt(i);
                mCurTags.removeAt(i);
                mCurTexts.removeAt(i);
                i--;
            }
            cnt++;
            //如果当前为离线查看模式，且正处于搜索进程中
            if (!isTerminalMode && isSearching) {
                if (isExitSearch) {
                    return;
                }
                int percent = (int)(percentMax * cnt / total);
                if (oldPercent != percent) {
                    setSearchPercentValue(prePercent + percent);
                }
                oldPercent = percent;
            }
        }
    }
    //如果存在搜索字符串
    if (str != "") {
        percentMax = (percentMax == 30) ? 30 : 60;
        prePercent = (percentMax == 30) ? 70 : 40;
        cnt = 0;
        for (int i = 0; i < mCurTexts.size(); ++i) {
            //将不包含搜索条件的日志从当前数据表中移除
            if (!isContainString(mCurTexts.at(i), str)) {
                mCurTimes.removeAt(i);
                mCurLevels.removeAt(i);
                mCurPIDs.removeAt(i);
                mCurTIDs.removeAt(i);
                mCurTags.removeAt(i);
                mCurTexts.removeAt(i);
                i--;
            }
            cnt++;
            //如果当前为离线查看模式，且正处于搜索进程中
            if (!isTerminalMode && isSearching) {
                if (isExitSearch) {
                    return;
                }
                int percent = (int)(percentMax * cnt / total);
                if (oldPercent != percent) {
                    setSearchPercentValue(prePercent + percent);
                }
                oldPercent = percent;
            }
        }
    }
    mCurLines = mCurTags.size();
    updateCurRange();   //更新当前显示缓冲区范围
    //自动更新时间过滤器的起止时间显示
    if (mCurTimes.isEmpty()) {
        mStartTime = "01-01 00:00:00.000";
        mStopTime = "01-01 00:00:00.000";
    } else {
        mStartTime = mCurTimes.at(0);
        mStopTime = mCurTimes.at(mCurLines - 1);
    }
    updateTimeStringToWidget(mStartTime, mStopTime);
    //显示当前数据表
    dispCurDataList();
}


/*
 * 函数名称：    autoAdjustTitleLabel()
 * 函数版本：        2.0.0
 * 作者：            HXL
 * 创建日期：    2017.3.14
 * 函数功能：    自动调节标签栏
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::autoAdjustTitleLabel() {
    if (mCurLines > 0) {
        QFontMetrics fm(ui->lwContent->font());
        //此处的6为固定的偏移，可视具体情况来调整
        labelLine->setMinimumWidth(6 + fm.width(QString::number(mCurLines)));
        labelLine->setMaximumWidth(6 + fm.width(QString::number(mCurLines)));
        labelTime->setMinimumWidth(fm.width("  " + mCurTimes.at(0)));
        labelTime->setMaximumWidth(fm.width("  " + mCurTimes.at(0)));
        labelLevel->setMinimumWidth(fm.width("   " + mCurLevels.at(0) + "  "));
        labelLevel->setMaximumWidth(fm.width("   " + mCurLevels.at(0) + "  "));
        labelPID->setMinimumWidth(fm.width("" + mCurPIDs.at(0)));
        labelPID->setMaximumWidth(fm.width("" + mCurPIDs.at(0)));
        labelTID->setMinimumWidth(fm.width("  " + mCurTIDs.at(0)));
        labelTID->setMaximumWidth(fm.width(" " + mCurTIDs.at(0)));
        labelTag->setMinimumWidth(fm.width("" + mCurTags.at(0)));
        labelTag->setMaximumWidth(fm.width("" + mCurTags.at(0)));
    }
}


/*
 * 函数名称：    intToString(int num, int bitNum)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.4
 * 函数功能：    将int数字转换为固定位数的QString，位数不足的补0
 * 输入参数：    num:int型数字  bitNum:固定的位数
 * 输出参数：    无
 * 返回值：      返回转换后的字符串
*/
QString MainWindow::intToString(int num, int bitNum) {
    return QString::number(num).rightJustified(bitNum, '0');
}


/*
 * 函数名称：    checkLogCatFile(QString str)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.4
 * 函数功能：    检查是否为Android的日志文件
 * 输入参数：    str:要检查的日志文件的第一行
 * 输出参数：    无
 * 返回值：      是的返回true，否则返回false
*/
bool MainWindow::checkLogCatFile(QString str) {
    if (mLogType == LOG_TYPE_UNKNOWN) {
        parseLogType(str);
        qDebug() << "mLogType: " << mLogType ;
        if (mLogType == LOG_TYPE_UNKNOWN) {
            return false;
        }
    }
    if (mLogType == LOG_TYPE_1) {
        bool match = regExp1.exactMatch(str);
        if (!match)   {
            return false;
        }
        reConstruction.clear();
        int pos = str.indexOf(": ");
        reConstruction.append(str.mid(0, pos));
        reConstruction.append(str.mid(pos + 2));
        return true;
    } else if (mLogType == LOG_TYPE_2) {
        return checkLogCatFileV2(str);
    }
}


/*
 * 函数名称：    checkLogCatFileV2(QString str)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.7.17
 * 函数功能：    检查是否为Android日志文件的V2版本
 * 输入参数：    str:要检查的日志文件的第一行
 * 输出参数：    无
 * 返回值：      是的返回true，否则返回false
*/
bool MainWindow::checkLogCatFileV2(QString str) {
    reConstruction.clear();
    QString ori = str.split(": ").at(0);
    if (ori == str) {
        return false;
    }
    QString temp = ori;
    while (temp.contains("  ")) {
        temp.replace("  ", " ");
    }
    if (temp.length() < 22) {
        return false;
    }
    if (':' == temp.at(21)) {
        temp.remove(21, 1);
    }
    QStringList tempList = temp.split(" ");
    if (tempList.size() < 6) {
        return false;
    }
    QString PID = tempList.at(2);
    QString TID = tempList.at(3);
    temp.remove(21 + PID.length() + 2, TID.length() + 1);
    reConstruction.append(temp.append(":"));
    reConstruction.append(str.mid(ori.length() + 2));
    QStringList list = temp.split(" ");
    if ('-' != list.at(0).at(2)) {
        reConstruction.clear();
        return false;
    }
    int num = list.at(1).split(":").length();
    if (num != 3) {
        reConstruction.clear();
        return false;
    }
    temp = list.at(2);
    if (!stringIsNum(temp)) {
        reConstruction.clear();
        return false;
    }
    if (!QString("VDIWEAF").contains(list.at(3))) {
        reConstruction.clear();
        return false;
    }
    return true;
}


/*
 * 函数名称：    parseLogType(QString str)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.7.17
 * 函数功能：    判断日志文件格式类型
 * 输入参数：    str:要检查的日志文件的第一行
 * 输出参数：    无
 * 返回值：      是的返回true，否则返回false
*/
void MainWindow::parseLogType(QString str) {
    bool match = regExp1.exactMatch(str);
    if (match) {
        mLogType = LOG_TYPE_1;
        return;
    }
    match = regExp2.exactMatch(str);
    if (match) {
        mLogType = LOG_TYPE_2;
        return;
    }
    match = regExp3.exactMatch(str);
    if (match) {
        mLogType = LOG_TYPE_3;
        return;
    }
    match = regExp4.exactMatch(str);
    if (match) {
        mLogType = LOG_TYPE_4;
        return;
    }
}


/*
 * 函数名称：    textToList(QString str)
 * 函数版本：        1.0.0
 * 函数版本：        1.0.1  【2017.4.21】修复部分PID号识别错误的BUG
 * 作者：            HXL
 * 创建日期：    2017.2.4
 * 函数功能：    解析日志文件的一行，提取时间、Tag、打印等级和日志内容等信息
 * 输入参数：    str:日志文件的一行
 * 输出参数：    无
 * 返回值：      返回解析后提取的信息
*/
QStringList MainWindow::textToList(QStringList str) {
    QString header = str.at(0);
//    qDebug() <<"textToList: header" <<header;
    QString content = str.at(1);
//    qDebug() <<"textToList: content" <<content;
    QStringList list;
    if (mLogType == LOG_TYPE_1) {
        QStringList rlist = header.split(QRegExp("\\s+"));
//        for(int i = 0; i< rlist.size(); i++) {
//            qDebug() << rlist[i];
//        }
        list.append(rlist[0]);
        list.append(rlist[1]);
        list.append(rlist[2]);
        list.append(rlist[3]);
        list.append(rlist[4]);
        list.append(rlist[5]);
        list.append(content);
        /*list.append(header.mid(0,18));                      //时间
        list.append((header.at(19) == 'F') ? 'A' : header.at(19));//打印等级
        int index = header.indexOf("(");
        QString temp = header.mid(index + 1).remove("):");
        int length = temp.length();
        for (int i = 0; i < length; ++i) {
            if (temp.at(length - i - 1) == '(') {
                index = index + length - i;
                break;
            }
        }
        list.append(header.mid(index + 1).remove("):"));    //PID进程号
        list.append(header.mid(21,index - 21));             //Tag
        list.append(content);  */                             //正文内容
    } else if (mLogType == LOG_TYPE_2) {
        list.append(header.mid(0, 21));                     //时间
        QStringList tempList = header.split(" ");
        list.append((tempList.at(3).at(0) == 'F') ? 'A' : tempList.at(3).at(0));//打印等级
        int index = header.indexOf("(");
        QString temp = header.mid(index + 1).remove("):");
        int length = temp.length();
        for (int i = 0; i < length; ++i) {
            if (temp.at(length - i - 1) == '(') {
                index = index + length - i;
                break;
            }
        }
        list.append(tempList.at(2));    //PID进程号
        temp = tempList.at(4);
        if (temp.contains(":")) {
            temp.mid(0, temp.length() - 1);
        }
        list.append(temp);             //Tag
//        list.append(content);          //正文内容
    }
    return list;
}


/*
 * 函数名称：    addItemToDataList(QString list)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.4
 * 函数功能：    对日志文件的一行进行解析，并将结果添加到数据表中
 * 输入参数：    list:日志文件一行解析后的数据结构
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::addItemToDataList(QStringList list) {
//    if (list.length() != 5) {
//        QMessageBox::information(this,"警告","添加项到数据表中出错，原因：解析数据后大小不为5！");
//        return;
//    }
    mAllTimes.append(list.at(1));
    mAllLevels.append(list.at(4));
    mAllPIDs.append(list.at(2));
    mAllTIDs.append(list.at(3));
    mAllTags.append(list.at(5));
    mAllTexts.append(list.at(6));
}


/*
 * 函数名称：    clearAllDataList()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.4
 * 函数功能：    初始化变量
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::clearAllDataList() {
    lwFilter->reset();
    lwFilter->clear();
    lwContent->reset();
    lwContent->clear();
    QObject::disconnect(ui->cbPID, SIGNAL(currentIndexChanged(int)), this, SLOT(cbPIDChangedSlot()));
    while (ui->cbPID->count() != 1) {
        ui->cbPID->removeItem(ui->cbPID->count() - 1);
    }
    QObject::connect(ui->cbPID, SIGNAL(currentIndexChanged(int)), this, SLOT(cbPIDChangedSlot()));
    mAllTimes.clear();
    mAllLevels.clear();
    mAllPIDs.clear();
    mAllTags.clear();
    mAllTexts.clear();
    mAllFilters.clear();
    mAllFiltersNums.clear();
    mPIDTypes.clear();
    mAllLines = 0;
    mCurFilters.clear();
    mCurFiltersNums.clear();
    clearCurDataList();
}


/*
 * 函数名称：    clearCurDataList()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.4
 * 函数功能：    清空当前数据表
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::clearCurDataList() {
    mCurTimes.clear();
    mCurLevels.clear();
    mCurPIDs.clear();
    mCurTIDs.clear();
    mCurTags.clear();
    mCurTexts.clear();
    mCurLines = 0;
}


/*
 * 函数名称：    loadLevelMessage(QString logLevel, QString logTag)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.4
 * 函数功能：    加载大于等于某一日志等级且指定Tag的所有日志信息
 * 输入参数：    logLevel:日志等级   logTag:Tag
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::loadLevelMessage(QString logLevel, QStringList tagList) {
    //用于搜索进程，该处占比40%
    int percentMax = 40;
    int oldPercent = 0;
    int curLevel = getLevelIndex(logLevel);
    int length = mAllLines;
    int start;
    QString curPID = ui->cbPID->currentText();
    //如果当前是实时终端模式则采用增量式更新当前数据库；
    //如果当前是离线查看模式则清空并重新建立当前数据库；
    if (isTerminalMode && !mFilterChangedFlag) {
        start = mAllLines - mAddNewLines;
        mAddNewLines = 0;
    } else {
        start = 0;
        clearCurDataList(); //先清空当前数据表
    }
    if (tagList.contains(DEFAULT_TAG)) {
        for (int i = start; i < length; i++) {
            if (getLevelIndex(mAllLevels.at(i)) >= curLevel) {
                if ((curPID == mAllPIDs.at(i)) || (curPID == "All")) {
                    mCurTimes.append(mAllTimes.at(i));
                    mCurLevels.append(mAllLevels.at(i));
                    mCurPIDs.append(mAllPIDs.at(i));
                    mCurTIDs.append(mAllTIDs.at(i));
                    mCurTags.append(mAllTags.at(i));
                    mCurTexts.append(mAllTexts.at(i));
                }
            }
            //如果当前为离线查看模式，且正处于搜索进程中
            if (!isTerminalMode && isSearching) {
                if (isExitSearch) {
                    return;
                }
                int percent = (int)(percentMax * (i + 1) / length);
                if (oldPercent != percent) {
                    setSearchPercentValue(percent);
                }
                oldPercent = percent;
            }
        }
    } else {
        for (int i = start; i < length; i++) {
            if (tagList.contains(mAllTags.at(i))) {
                if (getLevelIndex(mAllLevels.at(i)) >= curLevel) {
                    if ((curPID == mAllPIDs.at(i)) || (curPID == "All")) {
                        mCurTimes.append(mAllTimes.at(i));
                        mCurLevels.append(mAllLevels.at(i));
                        mCurPIDs.append(mAllPIDs.at(i));
                        mCurTIDs.append(mAllTIDs.at(i));
                        mCurTags.append(mAllTags.at(i));
                        mCurTexts.append(mAllTexts.at(i));
                    }
                }
            }
            //如果当前为离线查看模式，且正处于搜索进程中
            if (!isTerminalMode && isSearching) {
                if (isExitSearch) {
                    return;
                }
                int percent = (int)(percentMax * (i + 1) / length);
                if (oldPercent != percent) {
                    setSearchPercentValue(percent);
                }
                oldPercent = percent;
            }
        }
    }
//    if (logTag == DEFAULT_TAG) {
//    } else {
//    }
    mFilterChangedFlag = false;
}


/*
 * 函数名称：    filterTextHead()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.9
 * 函数功能：    将日志信息的定位信息头过滤掉（如果含有）
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      返回去掉定位信息头后的内容
*/
QString MainWindow::filterTextHead(QString text) {
    //定位信息头的格式为:"[ (xxx.java:xxx)#xxx ] "
    if (isContainHead(text)) {
        QString head = text.split(" ] ")[0] + " ] ";
        if (head != text) {
            return text.remove(head);
        } else {
            return text;
        }
    } else {
        return text;
    }
}


/*
 * 函数名称：    isContainHead()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.9
 * 函数功能：    判断日志信息内容是否含有定位信息头
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      包含返回true，否则返回false
*/
bool MainWindow::isContainHead(QString text) {
    //定位信息头的格式为:"[ (xxx.java:xxx)#xxx ] "
    if (!text.startsWith("[ (")) {
        return false;
    }
    if (text.contains(" ] ")) {
        QString tempStr = text.split(" ] ")[0].split("[ ")[1];
        if (tempStr.contains(".java:") && tempStr.contains(")#")) {
            return true;
        } else {
            return false;
        }
    } else {
        return false;
    }
}


/*
 * 函数名称：    isContainHead()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.9
 * 函数功能：    是否建立时间过滤器的信号与槽的连接
 * 输入参数：    isConnect:是否建立连接
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::isConnectTimeFilter(bool isConnect) {
    if (isConnect) {
        QObject::connect(ui->cbStartMonth, SIGNAL(currentIndexChanged(int)), this, SLOT(timeFilterChangedSlot()));
        QObject::connect(ui->cbStartDay, SIGNAL(currentIndexChanged(int)), this, SLOT(timeFilterChangedSlot()));
        QObject::connect(ui->cbStartHour, SIGNAL(currentIndexChanged(int)), this, SLOT(timeFilterChangedSlot()));
        QObject::connect(ui->cbStartMinute, SIGNAL(currentIndexChanged(int)), this, SLOT(timeFilterChangedSlot()));
        QObject::connect(ui->cbStartSecond, SIGNAL(currentIndexChanged(int)), this, SLOT(timeFilterChangedSlot()));
        QObject::connect(ui->cbStopMonth, SIGNAL(currentIndexChanged(int)), this, SLOT(timeFilterChangedSlot()));
        QObject::connect(ui->cbStopDay, SIGNAL(currentIndexChanged(int)), this, SLOT(timeFilterChangedSlot()));
        QObject::connect(ui->cbStopHour, SIGNAL(currentIndexChanged(int)), this, SLOT(timeFilterChangedSlot()));
        QObject::connect(ui->cbStopMinute, SIGNAL(currentIndexChanged(int)), this, SLOT(timeFilterChangedSlot()));
        QObject::connect(ui->cbStopSecond, SIGNAL(currentIndexChanged(int)), this, SLOT(timeFilterChangedSlot()));
    } else {
        QObject::disconnect(ui->cbStartMonth, SIGNAL(currentIndexChanged(int)), this, SLOT(timeFilterChangedSlot()));
        QObject::disconnect(ui->cbStartDay, SIGNAL(currentIndexChanged(int)), this, SLOT(timeFilterChangedSlot()));
        QObject::disconnect(ui->cbStartHour, SIGNAL(currentIndexChanged(int)), this, SLOT(timeFilterChangedSlot()));
        QObject::disconnect(ui->cbStartMinute, SIGNAL(currentIndexChanged(int)), this, SLOT(timeFilterChangedSlot()));
        QObject::disconnect(ui->cbStartSecond, SIGNAL(currentIndexChanged(int)), this, SLOT(timeFilterChangedSlot()));
        QObject::disconnect(ui->cbStopMonth, SIGNAL(currentIndexChanged(int)), this, SLOT(timeFilterChangedSlot()));
        QObject::disconnect(ui->cbStopDay, SIGNAL(currentIndexChanged(int)), this, SLOT(timeFilterChangedSlot()));
        QObject::disconnect(ui->cbStopHour, SIGNAL(currentIndexChanged(int)), this, SLOT(timeFilterChangedSlot()));
        QObject::disconnect(ui->cbStopMinute, SIGNAL(currentIndexChanged(int)), this, SLOT(timeFilterChangedSlot()));
        QObject::disconnect(ui->cbStopSecond, SIGNAL(currentIndexChanged(int)), this, SLOT(timeFilterChangedSlot()));
    }
}


/*
 * 函数名称：    isConnectScroll(bool isConnect)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.3.10
 * 函数功能：    是否建立内容显示区域滚动条的信号与槽的连接
 * 输入参数：    isConnect:连接或断开
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::isConnectScroll(bool isConnect) {
    if (isConnect) {
        QObject::connect(ui->lwContent->verticalScrollBar(), SIGNAL(valueChanged(int)),
                         this, SLOT(selfVerticalScrollSlot(int)));
        QObject::connect(ui->verticalScrollBar, SIGNAL(valueChanged(int)),
                         this, SLOT(verticalScrollSlot(int)));
        QObject::connect(ui->lwContent, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)),
                         this, SLOT(curContentChangedSlot()));
    } else {
        QObject::disconnect(ui->lwContent->verticalScrollBar(), SIGNAL(valueChanged(int)),
                            this, SLOT(selfVerticalScrollSlot(int)));
        QObject::disconnect(ui->verticalScrollBar, SIGNAL(valueChanged(int)),
                            this, SLOT(verticalScrollSlot(int)));
        QObject::disconnect(ui->lwContent, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)),
                            this, SLOT(curContentChangedSlot()));
    }
}


/*
 * 函数名称：    dispCurDataList()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.3.10
 * 函数功能：    显示当前的数据表
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::dispCurDataList() {
    //计算滚动条的最大值
    int max = mCurRange->getTotal() - mCurRange->getPageItemNum() + 1;
    //如果该值大于0则显示滚动条，否则隐藏
    if (max > 0) {
        ui->verticalScrollBar->setHidden(false);
        ui->verticalScrollBar->setRange(0, max);
    } else {
        ui->verticalScrollBar->setHidden(true);
        ui->verticalScrollBar->setRange(0, 0);
    }
    autoAdjustTitleLabel();     //自动调节标题标签的位置
    dispAreaData(mCurRange, 0); //将显示缓冲区的数据显示到屏幕
}


/*
 * 函数名称：    dispAreaData(ItemsRange *range, int direction)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.3.10
 * 函数功能：    将显示缓冲区中的数据显示到屏幕（所显示的内容由显示缓冲区范围决定）
 * 输入参数：    range:显示缓冲区范围   direction:滚动条滚动方向
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::dispAreaData(ItemsRange *range, int direction) {
    static bool flag = false;
    isConnectScroll(false);         //必须先断开滚动条的信号与槽的连接，否则将造成很多不必要的计算，浪费时间
    lwContent->clear();             //清空屏幕显示
    //获取当前数据表中的数据量，从而确定行号显示栏的位数
    int bitNum = QString::number(mCurLines).length();
    if (bitNum < 2) {
        bitNum = 2;
    }
    int first = range->getFirst();
    int last = range->getLast();
    logCurTime("显示范围：" + QString::number(first) + " ,"
               + QString::number(range->getVisibleFirst()) + " ,"
               + QString::number(last));
    QString tempText;
    QColor color;
    //根据缓冲区的范围显示对应的数据
    for (int i = first; i <= last; ++i) {
        tempText = intToString(i + 1, bitNum) + "  "
                   + ((isDispTime == true) ? (mCurTimes.at(i) + "  ") : "")
                   + ((isDispLevel == true) ? (mCurLevels.at(i) + "  ") : "")
                   + ((isDispPID == true) ? (mCurPIDs.at(i) + "  ") : "")
                   + ((isDispTID == true) ? (mCurTIDs.at(i) + "  ") : "")
                   + ((isDispTag == true) ? (mCurTags.at(i) + "    ") : "")
                   + ((isDispHead == true) ? (mCurTexts.at(i)) : filterTextHead(mCurTexts.at(i)));
        lwContent->addItem(tempText);
        color = mColorConfig->at(getLevelIndex(mCurLevels.at(i)));
        //将对应日志等级的文本设置成对应的颜色
        QListWidgetItem *item = lwContent->item(i - first);
        if (item == NULL) {
            continue;
        }
        item->setTextColor(color);
    }
    if (lwContent->count() > 0) {
        //如果是向下滑动，则将焦点设置在最后一行；否则，将焦点设置在第一行
        if (direction == 1) {
            lwContent->scrollToTop();
            lwContent->setCurrentRow(range->getVisibleFirst() + range->getPageItemNum() - 2 - first);
        } else {
            lwContent->scrollToBottom();
            lwContent->setCurrentRow(range->getVisibleFirst() - first);
        }
        //重新设定滚动条的位置
        ui->verticalScrollBar->setValue(range->getVisibleFirst());
    }
    //获取最新的内部滚动条的值，否则外部滚动条将会发生紊乱
    mLastScrollValue = lwContent->verticalScrollBar()->value();
    isConnectScroll(true);  //重新连接滚动条的信号与槽的连接
    curContentChangedSlot();
    //如果显示范围是：0,0,-1，且当前应该是有数据的，则立即恢复显示
    if ((first == 0) && (range->getVisibleFirst() == 0) && (last == -1)) {
        if ((mAllLines > 0) && (flag == false)) {
            flag = true;
            logCurTime("当前显示范围：0,0,-1，进一步检查是否真的无数据显示...");
//            qDebug()<<"【" + QDateTime::currentDateTime().toString("hh:mm:ss.zzz") + "】"
//                      + "当前显示范围：0,0,-1，进一步检查是否真的无数据显示...";
            dispContainSearchString(ui->etSearch->text());
        } else {
            flag = false;
        }
    } else {
        flag = false;
    }
}


/*
 * 函数名称：    isValidItemsRange(ItemsRange *range)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.3.10
 * 函数功能：    判断是否为一个有效的缓冲区范围
 * 输入参数：    range:显示缓冲区范围
 * 输出参数：    无
 * 返回值：      有效返回true，否则返回false
*/
bool MainWindow::isValidItemsRange(ItemsRange *range) {
    return (range->getLast() < mCurLines) && (range->getCount() > 0);
}


/*
 * 函数名称：    isDirExist(QString fullPath, bool isCreate)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.3.13
 * 函数功能：    判断一个路径是否存在，若不存在，可选择是否创建该路径
 * 输入参数：    fullPath:路径     isCreate:若不存在，是否创建
 * 输出参数：    无
 * 返回值：      路径存在返回true，否则返回false
*/
bool MainWindow::isDirExist(QString fullPath, bool isCreate) {
    QString path;
    QStringList temp = fullPath.split("/");
    if (temp.last().contains(".")) {
        path = fullPath.mid(0, fullPath.length() - temp.last().length() - 1);
    } else {
        path = fullPath;
    }
    QDir dir(path);
    if (dir.exists()) {
        return true;
    } else if (isCreate) {
        return dir.mkpath(path);
    } else {
        return false;
    }
}


/*
 * 函数名称：    mainThreadWait_ms(int ms)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.3.15
 * 函数功能：    使主线程阻塞一定的时间，目的是为了让所设置的UI界面得到及时的刷新和显示
 * 输入参数：    ms:要阻塞的毫秒数
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::mainThreadWait_ms(int ms) {
//    eventLoopTimer->start(ms);
//    mEventLoop->exec();
    QTime time = QTime::currentTime().addMSecs(ms);
    while (QTime::currentTime() < time) {
        QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
    }
}


/*
 * 函数名称：    addHistoryAction(QString path)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.3.16
 * 函数功能：    添加打开文件历史记录
 * 输入参数：    path:文件的绝对路径
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::addHistoryAction(QString path) {
    QList<QAction *> list = ui->history->actions();
    QMenu *history = ui->history;
    int size = list.size();
    //清空当前的所有Action，后面重新添加
    for (int i = 0; i < size; ++i) {
        history->removeAction(list.at(i));
    }
    //如果数量大于1，且倒数第二个为分隔符
    if ((size > 1) && (list.at(size - 2)->text().isEmpty())) {
        //如果数量大于等于了最大数量，则删除最旧的一个
        if ((MAX_SAVE_HISTORY_NUM > 0) && (size >= (2 + MAX_SAVE_HISTORY_NUM))) {
            list.removeAt(size - 3);
            size--;
        }
    }
    //添加最新的
    history->addAction(path);
    //添加之前的
    QString text = "";
    for (int i = 0; i < (size - 1); ++i) {
        text = list.at(i)->text();
        //如果不是分隔符，且不是当前打开的文件
        if (!text.isEmpty() && (text != path)) {
            history->addAction(text);
        }
    }
    //添加分隔符，之前的分隔符已经被去掉了
    history->addSeparator();
    //添加最后一个（即清空历史记录）
    history->addAction(list.at(size - 1)->text());
    mHistory.clear();
    list = history->actions();
    size = list.size() - 2;
    for (int i = 0; i < size; ++i) {
        text = list.at(i)->text();
        //logCurTime("history[" + QString::number(i) + "]=" + text);
        mHistory.append(text);
    }
    updateHistoryAction(mHistory);
    history->setEnabled(true);  //使能该选项（因为已经有历史记录存在了）
}


/*
 * 函数名称：    updateHistoryAction(QStringList history)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.3.17
 * 函数功能：    更新文件打开的历史记录
 * 输入参数：    history:历史记录
 * 输出参数：    无
 * 返回值：      无
*/
void MainWindow::updateHistoryAction(QStringList history) {
    QList<QAction *> list = ui->history->actions();
    QMenu *action = ui->history;
    int size = list.size();
    //清空当前的所有Action，后面重新添加
    for (int i = 0; i < size; ++i) {
        action->removeAction(list.at(i));
    }
    size = history.size();
    for (int i = 0; i < size; ++i) {
        action->addAction(history.at(i));
    }
    //添加分隔符
    action->addSeparator();
    //添加最后一个（即清空历史记录）
    action->addAction("清空历史记录");
    //将新的设置更新到设置文件中
    bool isSaveSuccess = mSettingParse->saveHistoryToFile(mSettingFilePath, mHistory);
    if (isSaveSuccess) {
        logCurTime("保存历史记录成功！");
    } else {
        logCurTime("保存历史记录失败！");
    }
}


/*
 * 函数名称：    isContainString(QString str, QString containStr)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.4
 * 函数功能：    判断在一个字符串中是否包含某一字符串
 * 输入参数：    str:要判断的字符串     containStr:是否包含的字符串
 * 输出参数：    无
 * 返回值：      包含返回true，否则返回false
*/
bool MainWindow::isContainString(QString str, QString containStr) {
    return str.contains(containStr, Qt::CaseInsensitive);
}


/*
 * 函数名称：    isContainTime()
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.9
 * 函数功能：    判断一个时间是否属于起止时间范围内
 * 输入参数：    无
 * 输出参数：    无
 * 返回值：      属于返回true，否则返回false
*/
bool MainWindow::isContainTime(QString time) {
    int result1 = QString::compare(time, mStartTime);
    int result2 = QString::compare(time, mStopTime);
    return (result1 >= 0) && (result2 <= 0);
}


/*
 * 函数名称：    getLevelIndex(QString logLevel)
 * 函数版本：        1.0.0
 * 作者：            HXL
 * 创建日期：    2017.2.4
 * 函数功能：    根据日志等级获取等级的索引值
 * 输入参数：    logLevel:日志等级
 * 输出参数：    无
 * 返回值：      等级的索引值
*/
int MainWindow::getLevelIndex(QString logLevel) {
    int levelIndex = 0;
    if ((logLevel == "V") || (logLevel == "verbose")) {
        levelIndex = 0;
    } else if (logLevel == "D" || (logLevel == "debug")) {
        levelIndex = 1;
    } else if (logLevel == "I" || (logLevel == "info")) {
        levelIndex = 2;
    } else if (logLevel == "W" || (logLevel == "warn")) {
        levelIndex = 3;
    } else if (logLevel == "E" || (logLevel == "error")) {
        levelIndex = 4;
    } else if (logLevel == "A" || (logLevel == "assert") || (logLevel == "F")) {
        levelIndex = 5;
    }
    return levelIndex;
}




